当在JavaScript中yield一个Promise时会发生什么?

7

没找到完整的答案...

当 Promise 被暂停时会发生什么?

这种结构是否正确?

var p = new Promise()
p.resolve(value)

function * (){
  yield p
}

等同于

function * (){
  yield value
}

更新

如何混合不同风格的异步编程,例如对于像koa这样的框架?

Koa中间件使用生成器进行工作,但是有很多优秀的基于promise的包(例如sequelize)。


有趣的事情只在于对生成器所产生的承诺进行的操作。它本身并没有什么特别的。 - Bergi
2个回答

6

当 Promise 被 yield 时会发生什么?

没有什么特别的。一个 Promise 只是一个对象。生成器将会 yield 这个 Promise,你可以订阅它:

var promise = generator().next().value;
promise.then(...);

6

正如 Felix 所说,promise 只是另一个可以被 yield 的值。

然而,有一种编写异步代码的风格,它以特定的方式利用了被 yield 的 promise。这包括一个周围的代码片段,它调用生成器,然后等待被 yield 的 promise 解决,只有在此之后再向生成器请求下一个值。这使得您可以将程序编写为:

function goGenerator *() {
  var userData = yield getUserData();
  yield checkuserData(userData);
}

getUserDatacheckUserData 都返回一个 Promise 时,代码会更加简洁,不需要再写如下代码:

function goPromises() {
  return getUserData() . then(checkUserData);
}

特别是涉及到更多承诺的情况下。基于生成器的风格是按顺序读取的,并且让人想起异步函数的方法。

async function goAsyncFunction() {
  var userData = await getUserData();
  return await checkUserData(userData);
}

但是异步函数的支持还不是很广泛。 基于生成器的方法是一种纯ES6可用的替代方案。

正如我所提到的,基于生成器的方法需要一个“包围”它的代码,知道如何处理yielded promises - 正如我所说的那样,等待它们解决后再次调用生成器。 这个的经典实现是co - 你可以搜索一下。 或者您也可以编写自己的:

function spawn(generator) {
  var iterator = generator();

  return new Promise(
    resolve =>
      function iterate(val) {
        var {value, done} = iterator.next(val);
        if (done) { resolve(val); }
        else { Promise.resolve(value).then(iterate); }
      }() 
  );

} 

现在你运行spawn(goGenerator)spawn本身返回一个承诺,所以你可以在它上面挂其他的东西:spawn(goGenerator) . then(doMoreStuff)
这是一个非常简单的实现。 co还有许多其他功能--例如,您可以yield一个承诺数组,它将等待所有承诺解析,类似于Promise.all

感谢您的好解释。我已经更新了问题。真正的问题不是如何将生成器转换为Promise,而是如何将Promise转换为生成器。 - akaRem
需要一个适当的生成器到Promise适配器才能在Promise上下文中使用生成器。 - vitaly-t
我不明白你所说的“将承诺转换为生成器”的意思。你能用简单易懂的语言解释一下吗? - user663031
为了完整性,请参阅此生成器到Promise适配器。还有一个需要考虑的异常处理方面。 - vitaly-t

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接