javascript async/await和ES6 yield与generator的区别

hpxqektj  于 2023-05-21  发布在  Java
关注(0)|答案(8)|浏览(157)

我刚刚阅读了一篇很棒的文章« Generators »,它清楚地强调了这个函数,它是处理生成器函数的助手函数:

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);
    }
  }
}
  • 我假设async关键字或多或少是用async/await实现的。* 所以问题是,如果是这样的话,那么await关键字和yield关键字之间的区别是什么?await是否总是将某些内容转换为promise,而yield则没有这样的保证?这是我最好的猜测!

您还可以在本文中看到async/await如何类似于yield的生成器,其中他描述了“spawn”函数ES7 async functions

siv3szwd

siv3szwd1#

事实证明,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关键字只是 Package 器函数,但如果是这种情况,那么await就变成了yield,当它们成为原生时,可能会有更多的图片。
你可以在这里看到更多的解释:https://www.promisejs.org/generators/

lsmd5eda

lsmd5eda2#

yield可以被认为是await的构建块。yield获取给定的值并将其传递给调用者。然后调用者可以对该值(1)做任何它希望做的事情。稍后,调用者可能会将一个值返回给生成器(通过generator.next()),该值将成为yield表达式(2)的结果,或者是一个似乎由yield表达式(3)抛出的错误。
async-await可以考虑使用yield。在⑴处,呼叫者(即,async-await驱动程序-类似于您发布的函数)将使用类似于new Promise(r => r(value)的算法将值 Package 在promise中(注意,* 不是 * Promise.resolve,但这不是什么大问题)。然后等待promise解析。如果满足,则在(2)处将满足的值传递回。如果它拒绝,则在(3)处将拒绝原因作为错误抛出。
因此async-await的效用是使用yield将产生的值作为promise展开并将其解析的值传递回去,重复直到函数返回其最终值。

hc8w905p

hc8w905p3#

await关键字和yield关键字有什么区别?
await关键字只能在async function s中使用,而yield关键字只能在generator function* s中使用。这两个函数显然也是不同的--一个返回promise,另一个返回generator。
await是否总是将某些内容转换为promise,而yield则没有这样的保证?
是的,await将调用Promise.resolve等待的值。
yield只产生生成器之外的值。

qxsslcnc

qxsslcnc4#

tl;dr

**使用async/await 99%的时间超过生成器。**为什么?

  1. async/await直接取代了最常见的promise链工作流,允许代码像同步一样声明,大大简化了它。
    1.生成器抽象了用例,在用例中,您将调用一系列相互依赖的异步操作,最终将处于“完成”状态。最简单的例子是分页遍历最终返回最后一个集合的结果,但您只会在需要时调用一个页面,而不是立即连续调用。
  2. async/await实际上是构建在生成器之上的一个抽象,以使promise的工作更容易。
    See very in-depth Explanation of Async/Await vs. Generators
k4ymrczo

k4ymrczo5#

尝试这个测试程序,我用它来理解await/async与承诺。

程序#1:如果没有promise,它就不会按顺序运行

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

ujv3wf0j6#

在许多方面,生成器是async/await的超集。目前async/await的堆栈跟踪比co更清晰,co是最流行的基于async/await的生成器。您可以使用生成器实现自己的异步/等待风格,并添加新功能,例如在非Promise上内置对yield的支持或在RxJS Observables上构建它。
因此,简而言之,生成器为您提供了更大的灵活性,基于生成器的库通常具有更多的功能。但是async/await是语言的核心部分,它是标准化的,不会在你的控制下改变,你不需要一个库来使用它。我有一个blog post,详细说明了async/await和generator之间的区别。

bqujaahr

bqujaahr7#

yield + gen.next()-as-a-language-feature可用于描述(或实现)await-async抽象出的底层控制流。

正如其他答案所暗示的那样,await-as-a-language-feature是(或者可以认为是)yield之上的实现。
这里有一个更直观的理解:
假设我们在一个异步函数await A -> await B -> ...中有42个awaits
实际上,它相当于拥有yield A -> tries resolve this as a Promise [1]
-> if resolvable, we yield B, and repeat [1] for B
-> if not resolveable, we throw
因此,我们最终在生成器中得到42 yields。在我们的控制器中,我们简单地继续执行gen.next(),直到它完成或被拒绝。(即这与在包含42个await的异步函数上使用await相同。
这就是为什么像redux-saga这样的lib利用generator将promise通过管道传递到saga中间件,以便在一个地方解析所有promise;从而将Promises构造与它们的求值分离,从而与Free Monad共享密切的相似性。

dnph8jn4

dnph8jn48#

其思想是递归地链接then()调用以复制await的行为,从而允许以同步方式调用async例程。生成器函数用于将控制权(和每个值)从被调用方返回给调用方,它恰好是_asyncToGenerator() Package 器函数。
如上所述,这是Babel用来创建polyfill的技巧。我稍微编辑了代码,使其更具可读性,并添加了注解。

(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 () {
    let gen = fn(); // Start the execution of the generator function and store the generator object.
    return new Promise(function (resolve, reject) {
      function step(func, arg) {
        try {
          let item = gen[func](arg); // Retrieve the function object from the property name and invoke it. Similar to eval(`gen.${func}(arg)`) but safer. If the next() method is called on the generator object, the item value by the generator function is saved and the generator resumes execution. The value passed as an argument is assigned as a result of a yield expression.
          if (item.done) {
            resolve(item.value);
            return; // The executor return value is ignored, but we need to stop the recursion here.
          }
          // The trick is that Promise.resolve() returns a promise object that is resolved with the value given as an argument. If that value is a promise object itself, then it's simply returned as is.
          return Promise.resolve(item.value).then(
            (v) => step("next", v),
            (e) => step("throw", e)
          );
        } catch (e) {
          reject(e);
          return;
        }
      }
      return step("next");
    });
  };
}

_asyncToGenerator(function* () { // <<< Now it's a generator function.
  const foo = yield 3; // <<< Now it's yield, not await.
  const bar = yield new Promise((resolve, reject) => resolve(7)); // <<< Each item is converted to a thenable object and recursively enclosed into chained then() calls.
  const baz = bar * foo;
  console.log(baz);
})();

相关问题