簡體   English   中英

ECMA6 生成器:產量 promise

[英]ECMA6 generators: yield promise

據我了解,ECMA6 生成器應該能夠屈服於返回 promise 的 function,最終返回已解決/拒絕。 讓代碼讀起來更像同步代碼,避免回調地獄。

我正在使用 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 }

我的代碼基於我能夠在網上找到的幾個示例。 我究竟做錯了什么?

據我了解,ECMA6生成器應該能夠屈服於一個返回promise的函數,最終返回已解決/拒絕的函數。

不,那不是他們的目的。 ES6生成器應該提供一種編寫迭代器的簡單方法 - 每次調用生成器函數都會產生一個迭代器。 迭代器只是一個值序列 - 就像一個數組,但是動態消耗並且懶惰地生成。

現在,生成器可以被濫用於異步控制流,通過生成一系列異步消耗的promise並使迭代器與每個等待的promise的結果一起推進。 請看這里沒有承諾的解釋。

所以你的代碼缺少的是實際等待承諾並推進你的生成器的消費者。 通常你會使用一個專用的庫(比如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);
    }());
}

現在使用此功能,您可以實際“執行”您的getPromise生成器:

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

tl; dr:發電機產生的承諾必須向前移動發電機。


如果您查看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!
}

但是既然你正在處理承諾,我們可以稍微改進一下。 y.next().value返回一個promise,你必須聽這個promise。 所以不要編寫console.log(y.next()) ,而是寫下:

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

巴貝爾演示

當然這不太實用,因為現在你無法訪問下一個產生的值而且你不知道何時完成發生器。 但是,您可以編寫一個遞歸函數來處理它。 這就是runGenerator在本文runGenerator介紹的內容。

我想出了以下示例來執行產生承諾的 function 生成器:

  • setTimeout 包含在 JavaScript Promise 中(參見睡眠實現)
  • 我們使用這個 Promise 來實現一個簡單的 Promise 鏈
  • 我們使用產生 Promise 的 function 生成器來表達它
  • 這是一個更大的帖子的總結這里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

 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