Starbeamrainbowlabs

Stardust
Blog

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.

Tag Cloud

3d account algorithms android announcement architecture archives arduino artificial intelligence artix assembly async audio backups bash batch blog bookmarklet booting c sharp c++ challenge chrome os code codepen coding conundrums coding conundrums evolved command line compilers compiling compression css dailyprogrammer debugging demystification distributed computing documentation downtime electronics email embedded systems encryption es6 features event experiment external first impressions future game github github gist gitlab graphics hardware hardware meetup holiday holidays html html5 html5 canvas infrastructure interfaces internet io.js jabber jam javascript js bin labs learning library linux lora low level lua maintenance manjaro network networking nibriboard node.js operating systems performance photos php pixelbot portable privacy problem solving programming problems projects prolog protocol protocols pseudo 3d python reddit redis reference releases resource review rust searching secrets security series list server software sorting source code control statistics storage svg technical terminal textures three thing game three.js tool tutorial twitter ubuntu university update updates upgrade version control virtual reality virtualisation visual web website windows windows 10 xmpp xslt

Archive

Art by Mythdael