Sự khác biệt giữa năng suất async / await và ES6 với máy phát điện


82

Tôi vừa mới đọc bài viết tuyệt vời này « Máy phát điện » và nó làm nổi bật rõ ràng chức năng này, đây là một chức năng trợ giúp để xử lý các chức năng của trình tạo:

function async(makeGenerator){
  return function () {
    var generator = makeGenerator.apply(this, arguments);

    function handle(result){
      // result => { done: [Boolean], value: [Object] }
      if (result.done) return Promise.resolve(result.value);

      return Promise.resolve(result.value).then(function (res){
        return handle(generator.next(res));
      }, function (err){
        return handle(generator.throw(err));
      });
    }

    try {
      return handle(generator.next());
    } catch (ex) {
      return Promise.reject(ex);
    }
  }
}

mà tôi giả thuyết ít nhiều là cách asynctriển khai từ khóa với async/ await. Vậy câu hỏi đặt ra là, nếu đúng như vậy, thì sự khác biệt giữa awaittừ khóa và từ khóa là cái quái yieldgì? Có phải awaitluôn luôn biến điều gì đó thành một lời hứa, trong khi yieldkhông đảm bảo như vậy? Đó là dự đoán tốt nhất của tôi!

Bạn cũng có thể thấy cách async/ awaittương tự như yieldvới máy phát điện trong bài viết này, nơi anh ấy mô tả chức năng 'sinh sản' các chức năng không đồng bộ ES7 .


1
hàm async -> một quy trình đăng quang. trình tạo -> trình lặp sử dụng một trình điều chỉnh để quản lý cơ chế lặp bên trong của nó. chờ đợi tạm ngưng một coroutine, trong khi năng suất trả lại kết quả từ một coroutine mà một số sử dụng máy phát điện
David Haim

1
async/awaitkhông phải là một phần của ES7. Vui lòng đọc mô tả thẻ.
Felix Kling,

Haim @ David, yeah nhưng async chờ được xây dựng trên đỉnh của máy phát điện vì vậy họ không khác biệt
Alexander Mills

Câu trả lời:


46

yieldcó thể được coi là khối xây dựng của await. yieldnhận giá trị mà nó đã cho và chuyển nó cho người gọi. Người gọi sau đó có thể làm bất cứ điều gì nó muốn với giá trị đó (1). Sau đó, người gọi có thể trả lại một giá trị cho trình tạo (qua generator.next()), giá trị này trở thành kết quả của yieldbiểu thức (2) hoặc một lỗi sẽ xuất hiện bởi yieldbiểu thức (3).

async- awaitcó thể được coi là sử dụng yield. Tại (1) người gọi (tức là async- awaittrình điều khiển - tương tự như hàm bạn đã đăng) sẽ bao bọc giá trị trong một lời hứa bằng cách sử dụng một thuật toán tương tự new Promise(r => r(value)(lưu ý, không phải Promise.resolve , nhưng đó không phải là vấn đề lớn). Sau đó nó sẽ đợi lời hứa để giải quyết. Nếu nó hoàn thành, nó sẽ chuyển giá trị đã hoàn thành trở lại (2). Nếu nó từ chối, nó sẽ đưa ra lý do từ chối như một lỗi tại (3).

Vì vậy, tiện ích của async- awaitlà máy móc này sử dụng yieldđể mở giá trị thu được như một lời hứa và chuyển lại giá trị đã phân giải của nó, lặp lại cho đến khi hàm trả về giá trị cuối cùng của nó.


kiểm tra câu trả lời này stackoverflow.com/a/39384160/3933557 mâu thuẫn với đối số này. async-await trông tương tự như lợi nhuận nhưng nó sử dụng chuỗi hứa hẹn dưới mui xe. Hãy chia sẻ nếu bạn có nguồn nào hay nói rằng "async-await có thể được coi là sử dụng lợi nhuận".
Samarendra

Tôi không chắc bạn đang coi câu trả lời đó là "mâu thuẫn với lập luận này" như thế nào, bởi vì nó nói điều tương tự với câu trả lời này. > Trong khi đó, các trình chuyển đổi như Babel cho phép bạn viết async / await và chuyển đổi mã thành trình tạo.
Arnavion

nó nói babel chuyển đổi thành máy phát điện nhưng những gì bạn đang nói là "năng suất có thể được coi là khối xây dựng của await" và "async-await có thể được coi là sử dụng năng suất". mà không đúng với sự hiểu biết của tôi (có thể sửa chữa). async-await nội bộ sử dụng chuỗi hứa hẹn như đã đề cập trong câu trả lời đó. Tôi muốn hiểu nếu có điều gì đó tôi còn thiếu, bạn có thể vui lòng chia sẻ suy nghĩ của bạn về điều này.
Samarendra

Câu trả lời này không đưa ra khẳng định rằng tất cả các động cơ ES trên toàn thế giới đều thực hiện các lời hứa trong nội bộ bằng cách sử dụng máy phát điện. Một số có thể; một số có thể không; nó không liên quan đến câu hỏi mà đây là một câu trả lời. Tuy nhiên, cách thức hoạt động của các hứa hẹn có thể được hiểu bằng cách sử dụng máy phát điện với một cách cụ thể để điều khiển máy phát và đó là những gì câu trả lời này giải thích.
Arnavion

43

Chà, hóa ra có một mối quan hệ rất chặt chẽ giữa async/ awaitvà máy phát điện. Và tôi tin rằng async/ awaitsẽ luôn được xây dựng trên máy phát điện. Nếu bạn nhìn vào cách Babel chuyển đổi async/ await:

Babel nhận điều này:

this.it('is a test', async function () {

    const foo = await 3;
    const bar = await new Promise(resolve => resolve('7'));
    const baz = bar * foo;
    console.log(baz);

});

và biến nó thành thế này

function _asyncToGenerator(fn) {
    return function () {
        var gen = fn.apply(this, arguments);
        return new Promise(function (resolve, reject) {
            function step(key, arg) {
                try {
                    var info = gen[key](arg);
                    var value = info.value;
                } catch (error) {
                    reject(error);
                    return;
                }
                if (info.done) {
                    resolve(value);
                } else {
                    return Promise.resolve(value).then(function (value) {
                        return step("next", value);
                    }, function (err) {
                        return step("throw", err);
                    });
                }
            }

            return step("next");
        });
    };
}


this.it('is a test', _asyncToGenerator(function* () {   // << now it's a generator

    const foo = yield 3;    //  <<< now it's yield, not await
    const bar = yield new Promise(resolve => resolve(7));
    const baz = bar * foo;
    console.log(baz);

}));

bạn làm toán.

Điều này làm cho nó trông giống như asynctừ khóa chỉ là hàm wrapper đó, nhưng nếu trường hợp đó xảy ra sau đó awaitchỉ được chuyển thành yield, có thể sẽ có nhiều hơn một chút cho bức tranh sau này khi chúng trở thành gốc.

Bạn có thể xem thêm giải thích cho vấn đề này tại đây: https://www.promisejs.org/generators/


1
NodeJS có async mẹ đẻ / chờ đợi trong một thời gian bây giờ, mà không cần máy phát điện: codeforgeek.com/2017/02/...
Bram

3
Triển khai gốc @Bram hoàn toàn sử dụng các trình tạo bên dưới, điều tương tự, chỉ được trừu tượng hóa.
Alexander Mills

3
Tôi không nghĩ vậy. Async / await được thực hiện nguyên bản trong động cơ V8. Máy phát điện có tính năng ES6, async / await là ES7. Nó là một phần của bản phát hành 5.5 của động cơ V8 (được sử dụng trong Node): v8project.blogspot.nl/2016/10/v8-release-55.html . Có thể chuyển ES7 async / await sang các trình tạo ES6, nhưng với các phiên bản mới của NodeJS, điều này không còn cần thiết nữa và hiệu suất của async / await thậm chí có vẻ tốt hơn so với các trình tạo: medium.com/@markherhold/…
Bram

1
async / sử dụng chờ đợi máy phát điện để làm điều này
Alexander Mills

@AlexanderMills, bạn có thể vui lòng chia sẻ một số tài nguyên hợp pháp nói rằng async / await sử dụng trình tạo nội bộ không? kiểm tra this ans stackoverflow.com/a/39384160/3933557 mâu thuẫn với đối số này. Tôi nghĩ, chỉ vì Babel sử dụng máy phát điện, không có nghĩa là nó được triển khai tương tự. Bất kỳ suy nghĩ nào về điều này
Samarendra

28

Sự khác biệt giữa awaittừ khóa và từ khóa là cái quái yieldgì?

Các awaittừ khóa được chỉ được sử dụng trong async functions, trong khi yieldtừ khóa chỉ được sử dụng trong máy phát điện function*s. Và những thứ đó rõ ràng cũng khác nhau - cái kia trả về lời hứa, cái kia trả về máy phát điện.

Có phải awaitluôn luôn biến điều gì đó thành một lời hứa, trong khi yieldkhông đảm bảo như vậy?

Có, awaitsẽ gọi Promise.resolvegiá trị đã chờ.

yield chỉ mang lại giá trị bên ngoài của trình tạo.


Một vấn đề nhỏ, nhưng như tôi đã đề cập trong câu trả lời của mình, thông số kỹ thuật không sử dụng Promise.resolve (nó đã từng sử dụng trước đó), nó sử dụng PromiseCapability :: phân giải được trình bày chính xác hơn bởi hàm tạo Promise.
Arnavion

@Arnavion: Promise.resolvesử dụng giống hệt như new PromiseCapability(%Promise%)thông số async / await sử dụng trực tiếp, tôi chỉ nghĩ Promise.resolvelà nên hiểu rõ hơn.
Bergi

1
Promise.resolvecó thêm "IsPromise == true? sau đó trả về cùng giá trị" ngắn mạch không đồng bộ không có. Đó là, await pđâu plà một lời hứa sẽ trả về một lời hứa mới giải quyết được p, Promise.resolve(p)ngược lại sẽ trở lại p.
Arnavion

Ồ, tôi đã bỏ lỡ điều đó - tôi nghĩ rằng điều này chỉ Promise.castđược sử dụng và không được chấp nhận vì lý do nhất quán. Nhưng không sao cả, dù sao chúng ta cũng không thực sự thấy lời hứa đó.
Bergi

2
var r = await p; console.log(r);nên được chuyển đổi thành một cái gì đó như :, p.then(console.log);while pcó thể được tạo thành :, var p = new Promise(resolve => setTimeout(resolve, 1000, 42));do đó, sai khi nói "await gọi Promise.resolve", nó là một số mã khác hoàn toàn khác xa biểu thức 'await' được gọi Promise.resolve, vì vậy awaitbiểu thức đã biến đổi , tức là Promise.then(console.log)sẽ được gọi và in ra 42.
Dejavu

14

tl; dr

Sử dụng async/ await99% thời gian so với máy phát điện. Tại sao?

  1. async/ awaittrực tiếp thay thế quy trình công việc phổ biến nhất của chuỗi hứa hẹn cho phép mã được khai báo như thể nó đồng bộ, đơn giản hóa đáng kể.

  2. Trình tạo tóm tắt trường hợp sử dụng trong đó bạn sẽ gọi một loạt các hoạt động không đồng bộ phụ thuộc vào nhau và cuối cùng sẽ ở trạng thái "hoàn thành". Ví dụ đơn giản nhất sẽ là phân trang thông qua các kết quả cuối cùng trả về tập hợp cuối cùng nhưng bạn sẽ chỉ gọi một trang khi cần thiết, không phải ngay lập tức liên tiếp.

  3. async/ awaitthực sự là một trừu tượng được xây dựng trên đầu máy tạo để làm cho việc làm việc với các hứa hẹn dễ dàng hơn.

Xem Giải thích rất chuyên sâu về Máy phát điện Async / Await vs.


4

Hãy thử các chương trình thử nghiệm này mà tôi đã từng hiểu await/ asyncvới những lời hứa.

Chương trình # 1: không có lời hứa, nó không chạy theo trình tự

function functionA() {
    console.log('functionA called');
    setTimeout(function() {
        console.log('functionA timeout called');
        return 10;
    }, 15000);

}

function functionB(valueA) {
    console.log('functionB called');
    setTimeout(function() {
        console.log('functionB timeout called = ' + valueA);
        return 20 + valueA;
    }, 10000);
}

function functionC(valueA, valueB) {

    console.log('functionC called');
    setTimeout(function() {
        console.log('functionC timeout called = ' + valueA);
        return valueA + valueB;
    }, 10000);

}

async function executeAsyncTask() {
    const valueA = await functionA();
    const valueB = await functionB(valueA);
    return functionC(valueA, valueB);
}
console.log('program started');
executeAsyncTask().then(function(response) {
    console.log('response called = ' + response);
});
console.log('program ended');

Chương trình # 2: với những lời hứa

function functionA() {
    return new Promise((resolve, reject) => {
        console.log('functionA called');
        setTimeout(function() {
            console.log('functionA timeout called');
            // return 10;
            return resolve(10);
        }, 15000);
    });   
}

function functionB(valueA) {
    return new Promise((resolve, reject) => {
        console.log('functionB called');
        setTimeout(function() {
            console.log('functionB timeout called = ' + valueA);
            return resolve(20 + valueA);
        }, 10000);

    });
}

function functionC(valueA, valueB) {
    return new Promise((resolve, reject) => {
        console.log('functionC called');
        setTimeout(function() {
            console.log('functionC timeout called = ' + valueA);
            return resolve(valueA + valueB);
        }, 10000);

    });
}

async function executeAsyncTask() {
    const valueA = await functionA();
    const valueB = await functionB(valueA);
    return functionC(valueA, valueB);
}
console.log('program started');
executeAsyncTask().then(function(response) {
    console.log('response called = ' + response);
});
console.log('program ended');

0

Theo nhiều cách, máy phát điện là một tập hợp lớn của async / await. Hiện tại async / await có dấu vết ngăn xếp rõ ràng hơn co , là lib dựa trên trình tạo giống async / await phổ biến nhất. Bạn có thể triển khai hương vị không đồng bộ / chờ đợi của riêng mình bằng cách sử dụng trình tạo và thêm các tính năng mới, chẳng hạn như hỗ trợ tích hợp cho yieldnhững người không hứa hẹn hoặc xây dựng nó trên các thiết bị quan sát RxJS.

Vì vậy, trong ngắn hạn, máy phát điện cung cấp cho bạn sự linh hoạt hơn và các lib dựa trên trình tạo thường có nhiều tính năng hơn. Nhưng async / await là một phần cốt lõi của ngôn ngữ, nó được chuẩn hóa và sẽ không thay đổi theo bạn, và bạn không cần thư viện để sử dụng nó. Tôi có một bài đăng trên blog với nhiều chi tiết hơn về sự khác biệt giữa async / await và máy phát điện.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.