91

我刚刚阅读了这篇精彩的文章 « 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实现关键字的方式。asyncawaitawait所以问题是,如果是这样的话,那么关键字和关键字之间到底有什么区别yieldawait总是把某事变成一个承诺,而yield没有做出这样的保证吗?这是我最好的猜测!

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

4

8 回答 8

50

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

巴别塔接受这个:

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/

于 2016-03-27T08:10:35.403 回答
48

yield可以被认为是await. yield获取它给出的值并将其传递给调用者。然后,调用者可以使用该值 (1) 为所欲为。稍后,调用者可能会将一个值返回给生成器(通过generator.next()),该值将成为yield表达式 (2) 的结果,或者看起来会由yield表达式 (3) 抛出的错误。

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

所以async-的效用await是这种机制,用于yield将产生的值作为承诺解包并将其解析的值传回,重复直到函数返回其最终值。

于 2016-03-24T11:23:36.497 回答
32

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

await关键字只能在async functions 中使用,而关键字yield只能在 generator 中使用function*。这些也明显不同——一个返回承诺,另一个返回生成器。

await总是把某事变成一个承诺,而yield没有做出这样的保证吗?

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

yield只产生生成器之外的值。

于 2016-03-24T11:26:10.453 回答
22

tl;博士

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

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

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

  3. async/await实际上是建立在生成器之上的抽象,以使使用 Promise 更容易。

查看 Async/Await 与生成器的非常深入的解释

于 2018-07-23T17:46:56.433 回答
6

试试这个我曾经理解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');

于 2018-06-14T05:20:32.533 回答
0

在许多方面,生成器是 async/await 的超集。现在 async/await 的堆栈跟踪比co更干净,co是最流行的基于 async/await 的生成器库。您可以使用生成器实现自己的 async/await 风格并添加新功能,例如内置支持yieldon non-promises 或在 RxJS observables 上构建它。

因此,简而言之,生成器为您提供了更大的灵活性,并且基于生成器的库通常具有更多功能。但是 async/await 是该语言的核心部分,它是标准化的,不会随你改变,你也不需要库来使用它。我有一篇文,详细介绍了异步/等待和生成器之间的区别。

于 2018-05-03T16:03:01.360 回答
0

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


正如其他答案所暗示的那样,await-as-a-language-feature 是(或可以被认为是)在yield.

这是一个更直观的理解:

假设我们awaits在异步函数中有 42 个, await A -> await B -> ...

在内心深处,它相当于拥有yield A -> tries resolve this as a Promise[1]

-> if resolvable, we yield B, and repeat [1] for B

-> if not resolveable, we throw

所以我们最终yields在一个生成器中得到了 42 个。在我们的控制器中,我们只是继续做gen.next(),直到它完成或被拒绝。await(即,这与在包含 42 的异步函数上使用相同await。)

这就是为什么像 redux-saga 这样的 lib 使用生成器然后将 Promise 传递到 saga 中间件以在一个地方全部解析的原因;从而将 Promise 构造与它们的评估脱钩,从而与Free Monad非常相似。

于 2021-09-04T11:59:10.937 回答
0

这个想法是递归链接then()调用以复制其行为await,允许以async同步方式调用例程。生成器函数用于将控制(和每个值)从被调用者返回给调用者,这恰好是_asyncToGenerator()包装函数。

如上所述,这是 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);
})();
于 2022-01-23T21:11:52.440 回答