ES6 Features 3.5: More Generators
While following the rest of David Walsh's generator tutorial (link here!), I discovered a really neat trick that combines both Promises with generators. Basically you can write a promise based function and then yield it's return value. Then if you write an engine function that runs a generator to completion that passes in the yield value via Iterator.next, you can use an asynchronous function as if it were synchronous:
function do_work()
{
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(Math.random());
}, 1000);
});
}
function do_hard_work()
{
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(Math.random());
}, 3000);
});
}
function *async_test()
{
var number1 = yield do_work();
console.log(`Number 1: ${number1}`);
var number2 = yield do_hard_work();
console.log(`Number 2: ${number2}`);
console.log(`Result: ${number1 + number2}`);
}
In the above example, I have 2 promise-based asynchronous functions called do_work() and do_hard_work(). In reality you would have some functions that were actually useful (e.g. that performed a GET request and returned the result, for example), but this is just a demonstration. The generator yields the return value of the two async functions.
A function something like this can be used to run the generator:
// run (async) a generator to completion
// Note: simplified approach: no error handling here
function run_generator(g) {
var it = g(), ret;
// asynchronously iterate over generator
(function iterate(val){
ret = it.next( val );
if (!ret.done) {
// poor man's "is it a promise?" test
if ("then" in ret.value) {
// wait on the promise
ret.value.then( iterate );
}
// immediate value: just send right back in
else {
// avoid synchronous recursion
setTimeout( function(){
iterate( ret.value );
}, 0 );
}
}
})();
}
(credit: David Walsh's Going Async With ES6 Generators)
If you ran the above generator you might get something like this:
$iojs async-generators.js
Number 1: 0.07386422459967434
Number 2: 0.7089381434489042
Result: 0.7828023680485785
How cool is that?! This will simplify complex async javascript so much. I think I am going to put this technique to work in my next rewrite of Bloworm's web based client...
Suggestions for next week's ES6 feature 'tutorial' are welcome in the comments.