简体   繁体   English

ECMA6 生成器:产量 promise

[英]ECMA6 generators: yield promise

As I understand it ECMA6 generators are supposed to be able to yield to a function that returns a promise, eventually returning the resolved/rejected.据我了解,ECMA6 生成器应该能够屈服于返回 promise 的 function,最终返回已解决/拒绝。 Making the code read more like synchronous code, and avoiding callback hell.让代码读起来更像同步代码,避免回调地狱。

I am using node.js v0.12.2 with --harmony and the following code.我正在使用 node.js v0.12.2 和 --harmony 和以下代码。

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 }

I've based the code off of the few examples I have been able to find online.我的代码基于我能够在网上找到的几个示例。 What am I doing wrong?我究竟做错了什么?

As I understand it ECMA6 generators are supposed to be able to yield to a function that returns a promise, eventually returning the resolved/rejected. 据我了解,ECMA6生成器应该能够屈服于一个返回promise的函数,最终返回已解决/拒绝的函数。

No, that's not their purpose. 不,那不是他们的目的。 ES6 generators are supposed to provide a simple way for writing iterators - each call to a generator function produces an iterator. ES6生成器应该提供一种编写迭代器的简单方法 - 每次调用生成器函数都会产生一个迭代器。 An iterator is just a sequence of values - like an array, but consumed dynamically and produced lazily. 迭代器只是一个值序列 - 就像一个数组,但是动态消耗并且懒惰地生成。

Now, generators can be abused for asynchronous control flow, by producing a sequence of promises that is consumed asynchronously and advancing the iterator with the results of each awaited promise. 现在,生成器可以被滥用于异步控制流,通过生成一系列异步消耗的promise并使迭代器与每个等待的promise的结果一起推进。 See here for an explanation without promises. 请看这里没有承诺的解释。

So what your code is missing is the consumer that actually waits for the promises and advances your generator. 所以你的代码缺少的是实际等待承诺并推进你的生成器的消费者。 Usually you'd use a dedicated library (like co or task.js ), or one of the helper functions that many promise libraries provide ( Q , Bluebird , when , …), but for the purposes of this answer I'll show a simplified one: 通常你会使用一个专用的库(比如cotask.js ),或许多承诺库提供的辅助函数之一( 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);
    }());
}

Now with this function you can actually "execute" your getPromise generator: 现在使用此功能,您可以实际“执行”您的getPromise生成器:

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

tl;dr: The promise yielded by the generator has to move the generator forward. tl; dr:发电机产生的承诺必须向前移动发电机。


If you look at first examples in http://davidwalsh.name/async-generators , you will notice that the async function actually moves the iterator forward: 如果您查看http://davidwalsh.name/async-generators中的第一个示例,您会注意到async函数实际上向前移动了迭代器:

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!
}

But since you are working with promises, we can improve on that, a little bit. 但是既然你正在处理承诺,我们可以稍微改进一下。 y.next().value returns a promise, and you'd have to listen to that promise. y.next().value返回一个promise,你必须听这个promise。 So instead of writing console.log(y.next()) , you'd write: 所以不要编写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 巴贝尔演示

Of course this not very practical, because now you cannot access the next yielded value and you don't know when the generator will be done. 当然这不太实用,因为现在你无法访问下一个产生的值而且你不知道何时完成发生器。 However, you could write a recursive function which takes care of that. 但是,您可以编写一个递归函数来处理它。 That's what runGenerator introduced later in this article takes care of. 这就是runGenerator在本文runGenerator介绍的内容。

I came up with the following example for executing function generators that yield promises:我想出了以下示例来执行产生承诺的 function 生成器:

  • setTimeout is wrapped up in a JavaScript Promise (see sleep implementation) setTimeout 包含在 JavaScript Promise 中(参见睡眠实现)
  • we use this Promise to implement a simple Promise chain我们使用这个 Promise 来实现一个简单的 Promise 链
  • we express it using function generators that yield Promises我们使用产生 Promise 的 function 生成器来表达它
  • this is a summary of a larger post here ES6 asynchronous generator result这是一个更大的帖子的总结这里ES6 异步生成器结果
  • this implementation using an _asyncToGenerator() helper function that was derived from https://babeljs.io transpiler for converting async/await syntax to function generator yield syntax this implementation using an _asyncToGenerator() helper function that was derived from https://babeljs.io transpiler for converting async/await syntax to function generator yield syntax

 function sleep(delay) { return new Promise(function (resolve, reject) { setTimeout(resolve, delay); } ); } _asyncToGenerator(function *() { console.log('one'); yield sleep(1000); console.log('two'); yield sleep(1000); console.log('three'); })(); function _asyncToGenerator(fn) { return function() { var self = this, args = arguments return new Promise(function(resolve, reject) { var gen = fn.apply(self, args) function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value) } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err) } _next(undefined) }) } } function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg) var value = info.value } catch (error) { reject(error) return } if (info.done) { resolve(value) } else { Promise.resolve(value).then(_next, _throw) } }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM