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. 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.
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.
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. 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. 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:
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:
run(getPromise).then(console.log, console.error);
tl;dr: The promise yielded by the generator has to move the generator forward.
If you look at first examples in http://davidwalsh.name/async-generators, you will notice that the async function actually moves the iterator forward:
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. So instead of writing console.log(y.next())
, you'd write:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With