【问题标题】:ECMA6 generators: yield promiseECMA6 生成器:产量承诺
【发布时间】:2021-10-28 05:30:15
【问题描述】:

据我了解,ECMA6 生成器应该能够屈服于返回承诺的函数,最终返回已解决/拒绝的函数。让代码读起来更像同步代码,避免回调地狱。

我正在使用带有 --harmony 和以下代码的 node.js v0.12.2。

var someAsyncThing = function() {
  return new Promise(function(resolve, reject) {
    resolve("I'm Resolved!");
  });
};

someAsyncThing().then(function(res) {console.log(res);});
// Works as expected: logs I'm Resolved!

function* getPromise() {
    var x = yield someAsyncThing();
    console.log("x: " + x); // Fails x undefined
}

var y = getPromise();
console.log(y); // returns {}

console.log(y.next());
// Fails: logs { value: {}, done: false }

我的代码基于我能够在网上找到的几个示例。我做错了什么?

【问题讨论】:

  • 你应该阅读这篇文章:davidwalsh.name/async-generators
  • 我确实读过,这段代码部分基于他的示例。它不起作用。
  • 你错过了那篇文章中最重要的部分,runGenerator 函数。
  • runGenerator 只是他自己的装饰器,不需要。他展示的第一个例子不需要它,他是这么说的。也没有其他示例需要以这种方式使用生成器。
  • 很公平。看我的回答。

标签: javascript node.js ecmascript-6


【解决方案1】:

据我了解,ECMA6 生成器应该能够屈服于返回承诺的函数,最终返回已解决/已拒绝。

不,这不是他们的目的。 ES6 生成器应该提供一种编写迭代器的简单方法——每次调用生成器函数都会产生一个迭代器。迭代器只是一个值序列 - 就像一个数组,但动态消耗并延迟生成。

现在,生成器可以被滥用用于异步控制流,方法是生成一系列异步使用的 Promise,并使用每个等待的 Promise 的结果推进迭代器。请参阅here 以获得没有承诺的解释。

因此,您的代码缺少的是实际等待承诺并推进您的生成器的消费者。通常你会使用一个专用库(如cotask.js),或许多promise 库提供的辅助函数之一(QBluebirdwhen,...),但为了这些目的这个答案我会展示一个简化的:

function run(gf) {
    let g = gf();
    return Promise.resolve(function step(v) {
         var res = g.next(v);
         if (res.done) return res.value;
         return res.value.then(step);
    }());
}

现在有了这个函数,你实际上可以“执行”你的getPromise 生成器:

run(getPromise).then(console.log, console.error);

【讨论】:

    【解决方案2】:

    tl;dr:生成器产生的承诺必须推动生成器前进。


    如果您查看http://davidwalsh.name/async-generators 中的第一个示例,您会注意到异步函数实际上将迭代器向前移动:

    function request(url) {
        // this is where we're hiding the asynchronicity,
        // away from the main code of our generator
        // `it.next(..)` is the generator's iterator-resume
        // call
        makeAjaxCall( url, function(response){
            it.next( response );               // <--- this is where the magic happens
        } );
        // Note: nothing returned here!
    }
    

    但是由于您使用的是 Promise,因此我们可以稍微改进一下。 y.next().value 返回一个承诺,你必须听那个承诺。所以不要写console.log(y.next()),你应该写:

    var promise = y.next().value;
    promise.then(y.next.bind(y)); // or promise.then(function(v) { y.next(v); });
    

    Babel demo

    当然这不是很实用,因为现在您无法访问下一个产生的值,并且您不知道生成器何时完成。但是,您可以编写一个递归函数来处理这个问题。这就是本文后面介绍的runGenerator 的作用。

    【讨论】:

      猜你喜欢
      • 2020-10-05
      • 2013-11-16
      • 2021-12-30
      • 2017-10-20
      • 2019-07-17
      • 2023-03-17
      • 1970-01-01
      • 2018-05-07
      相关资源
      最近更新 更多