Introduction

Small, light, fast generator-based async control flow for ES6. gentrified code can invoke async functions as if they were synchronous. No more callback hell.

gentrify is only about 40 lines, and fits nicely in your brain's L-1 cache. It is about 10x lower overhead than similar solutions with promises. Async call stack depth can exceed a million without hitting stack overflow. Explicit tail calls are optimized.

This project is an experiment; caveat user.

Examples

NOTE: The examples below will only work on browsers which support a reasonable subset of ES6. As of late 2016 include desktop browsers like Chrome 53.0+, Firefox 48.0+ and Safari 10.0+

var topics = ["generator", "es6"]; function* githubSearch(topics) { for (var t of topics) { // invoke a promise-based API var data = yield $.getJSON(`https://api.github.com/search/repositories?q=${t}&sort=stars`); console.log(data.items.slice(0, 5).map(x => `${x.full_name},${x.stargazers_count}`).join('\n')); console.log("sleeping 3 seconds..."); // invoke another async function yield sleep(3000); } return "done!"; } function* sleep(ms) { // invoke a callback-based API return yield cb => setTimeout(cb, ms); } gentrify.run(githubSearch(topics)) .then(x => console.log(x)) .catch(x => console.log("Error", x));

Performance

Consider a recursive (generator) function to compute the sum of the first n integers. This is not a real-world use case. The idea is to to measure the "sync tax" we pay for using such a library.

function* sumn(x) {
    return x <= 0 ? 0 : x + (yield sumn(x - 1));
}

Here we try the popular co library

var n = 1000; var start = now(); co(sumn(n)).then(x => console.log("co: ", x, now() - start, "ms"), x => console.log("co: Boom!", x.message));

gentrify the same scenario

var n = 1000; var start = now(); gentrify.run(sumn(n)).then(x => console.log("gentrify: ", x, now() - start, "ms"), x => console.log("gentrify: Boom!", x.message));

Stack Overflow

Try increasing the value of n in the above two examples. You'll find that co runs into a stack overflow (or a tab crash) at a little beyond 1500. gentrifycontinues well above a million. Eventually you run out of memory and get a tab crash.

Tail Call Optimization

With a tail-recursive version of sumn(), we can recurse to infinity and beyond. Wrapping the returned generator with gentrify.tc() explicitly tells gentrify.run() that the returned value should be treated as a tail call.

function* tsumn(x, acc=0) {
    return x <= 0 ? acc : tc(tsumn(x - 1, acc + x));
}
var n = 1000000; var start = now(); gentrify.run(tsumn(n)).then(x => console.log("gentrify: ", x, now() - start, "ms"), x => console.log("gentrify: Boom!", x.message));

Documentation

See Github for API documentation.