生成器的异步/等待和ES6收益之间的区别


82

我刚刚读了这篇很棒的文章《生成器》,它清楚地突出了此函数,它是处理生成器函数的辅助函数:

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);
    }
  }
}

我想大概是asyncasync/实现关键字的方式await所以问题是,如果是这样,那么await关键字和yield关键字之间的区别到底是什么?是否await总是把东西放到一个承诺,而yield没有这样的保证?那是我最好的猜测!

您还可以在本文中看到生成器与async/与生成器的await相似之处,yield其中他介绍了“生成”功能ES7异步功能


1
异步功能->协程。generator-> Iterator,它使用协程来管理其内部迭代机制。等待暂停协程,而产量返回某些发电机使用的协程的结果
David Haim

1
async/await不属于ES7。请阅读标签说明。
Felix Kling

@david haim,是的,但是异步等待器是基于生成器构建的,因此它们并不明显
Alexander Mills

Answers:


46

yield可以被认为是awaityield接受给定的值并将其传递给调用方。然后,调用者可以使用该值(1)进行任何所需的操作。稍后,调用者可以将值返回给生成器(通过generator.next()),该值成为yield表达式(2)的结果,或者是错误似乎由yield表达式(3)引发。

async-await可以考虑使用yield。在(1)处,调用者(即async-await驱动程序-与您发布的函数类似)将使用与new Promise(r => r(value)(注意,不是 Promise.resolve,但这没什么大不了)类似的算法将值包装在promise中。然后,它等待诺言解决。如果满足,则将满足的值传递回(2)。如果拒绝,则将拒绝原因作为错误抛出(3)。

因此,async-的实用程序awaityield用来解开产生值作为承诺并将其解析值传递回去的机器,重复执行直到函数返回其最终值为止。


检查此答案stackoverflow.com/a/39384160/3933557与该论点相矛盾。async-await看起来与yield相似,但是它在后台使用了promise链。如果您有任何好的资源,请分享“可以将async-await视为使用yield”。
Samarendra

我不确定您如何看待这个答案是“矛盾的”,因为它与这个答案说的是一样的话。>同时,像Babel这样的编译器使您可以编写async / await并将代码转换为生成器。
Arnavion

它说babel转换为发电机,但是您要说的是“ yield可以被认为是await的基础”和“ async-await可以被认为是使用yield”。根据我的理解,这是不正确的(可能会更正)。异步等待在内部使用承诺链,如该答案中所述。我想了解是否有我想念的东西,请您分享一下您的想法。
Samarendra

这个答案并不能说全世界的所有ES引擎都在内部使用生成器来实现承诺。有些可能;有些可能不会;这是否是答案与这个问题无关。不过,使用发电机以特定的方式驱动发电机可以理解承诺工作的方式,这就是这个答案的解释。
Arnavion

43

好吧,事实证明async/await与生成器之间存在非常密切的关系。我相信async/await将永远建立在发电机上。如果您查看Babel转换方式async/ await

Babel采取以下措施:

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);

});

并变成它

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);

}));

你做数学。

这看起来好像async关键字只是那个包装函数,但是如果是这种情况,那么await就变成了yield,当它们成为本机时,图片可能还会有更多的变化。

您可以在这里查看更多对此的解释:https : //www.promisejs.org/generators/



3
@Bram本地实现绝对使用引擎内部的东西,同样的东西,刚刚被抽象掉了。
亚历山大·米尔斯

3
我不这么认为。异步/等待是在V8引擎中本地实现的。具有ES6功能(异步/等待)的生成器是ES7。它是V8引擎5.5版本(在Node中使用)的一部分:v8project.blogspot.nl/2016/10/v8-release-55.html。它可以transpile ES7异步/ AWAIT到ES6发电机,但与中的NodeJS新版此不再需要,和异步/的await甚至似乎是更好然后发电机的性能:medium.com/@markherhold/...
布拉姆

1
异步/等待使用发电机来完成它的工作
Alexander Mills

@AlexanderMills能否请您分享一些合法资源,这些资源说async / await在内部使用了生成器?请检查此an stackoverflow.com/a/39384160/3933557,是否与此参数相矛盾。我认为,仅仅因为Babel使用了生成器,并不意味着它在后台被类似地实现。任何想法
Samarendra

28

await关键字和yield关键字之间有什么区别?

await关键字仅在使用async functionS,而yield关键字是唯一的在发电机使用function*秒。而且这些显然也不同-一个回报承诺,另一个回报生成器。

是否await总是把东西放到一个承诺,而yield没有这样的保证?

是的,await将调用Promise.resolve等待的值。

yield 只是在生成器之外产生值。


轻微的问题,但是正如我在回答中提到的,该规范不使用Promise.resolve(它以前使用过),而是使用PromiseCapability :: resolve,它由Promise构造函数更准确地表示。
Arnavion

@Arnavion:Promise.resolve使用new PromiseCapability(%Promise%)与async / await规范直接使用的完全相同,我只是觉得Promise.resolve更好理解。
Bergi

1
Promise.resolve有一个额外的“ IsPromise == true?然后返回相同的值”短路,异步没有此短路。也就是说,一个Promise将await p在哪里p返回解析为的新的Promise p,而Promise.resolve(p)将返回p
Arnavion

哦,我很想念-我认为这只是Promise.cast出于一致性原因而已弃用。但这并不重要,无论如何我们都不会真正看到这个承诺。
Bergi

2
var r = await p; console.log(r);应该转换为类似:p.then(console.log);,尽管p可能会创建为:var p = new Promise(resolve => setTimeout(resolve, 1000, 42));,所以说“ await调用Promise.resolve”是错误的,它与调用的'await'表达式完全相去的其他代码Promise.resolve,因此转换后的await表达式,即Promise.then(console.log)会被调用并打印出来42
Dejavu

14

tl; dr

在发电机上使用async/ await99%的时间。为什么?

  1. async/await直接替换了最常见的承诺链工作流,从而允许将代码声明为同步的,从而大大简化了代码。

  2. 生成器将用例抽象化,您将调用一系列相互依赖的异步操作,最终将处于“完成”状态。最简单的示例是分页显示最终返回最后一组结果的结果,但您只会根据需要调用页面,而不是立即连续调用。

  3. async/await实际上是建立在生成器之上的抽象,以使兑现承诺更加容易。

深入了解Async / Await与生成器的说明


4

试试这个我曾经理解过的await/async有希望的测试程序。

程序#1:不保证不会按顺序运行

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');

计划2:承诺

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

在许多方面,生成器是异步/等待的超集。现在,与基于最常见的基于async / await的生成器的lib库co相比,现在async / await的堆栈跟踪更整洁。您可以使用生成器实现自己的异步/等待风格,并添加新功能,例如yield对非承诺的内置支持或在RxJS可观察对象上的内置支持。

简而言之,生成器为您提供了更大的灵活性,基于生成器的库通常具有更多功能。但是async / await是语言的核心部分,它是标准化的,在您的指导下不会更改,并且您不需要库即可使用它。我有一篇博客文章,其中详细介绍了异步/等待和生成器之间的区别。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.