This vanilla ES6 function async
allows code to yield
(i.e. await
) the asynchronous result of any Promise
within. The usage is almost identical to ES7's async/await
keywords.
async/await
control flow is promising because it allows the programmer to reason linearly about complex asynchronous code. It also has the benefit of unifying traditionally disparate synchronous and asynchronous error handling code into one try/catch block.
This is expository code for the purpose of learning ES6. It is not 100% robust. If you want to use this style of code in the real world you might want to explore a well-tested library like co, task.js or use async/await
with Babel. Also take a look at the official async/await
draft section on desugaring.
- node.js - 4.3.2+ (maybe earlier with --harmony flag)
- browser - (if you have Promises && Generators)
- babel 👍
{ "presets": ["es2015"] }
- async/await draft - desugaring
- Promises
- Iterators & Generators
- Basic of ES6 Generators by Kyle Sympson
- Return
Promise
fromasync
to support chaining - Support other asynchronous actions like thunks or nested generators
/**
* @param {Generator} gen Generator which yields Promises
* @param {Any} context last resolved Promise value, currently only used recursively
*/
function async(gen, context = undefined) {
const generator = typeof gen === 'function' ? gen() : gen; // Create generator if necessary
const { value: promise } = generator.next(context); // Pass last result, get next Promise
if ( typeof promise !== 'undefined' ) {
promise.then(resolved => async(generator, resolved))
.catch(error => generator.throw(error)); // Defer to generator error handling
}
}
/* Usage */
async(function* () { // Generators can't be declared with arrow syntax
try {
// Execution is paused until the yielded promise resolves
console.log(yield Promise.resolve('A Mortynight Run'))
// Promises already provide support for concurrent async actions.
// Execution will not continue until both a & b are fulfilled
const [a,b] = yield Promise.all([
Promise.resolve('Get Schwifty'),
Promise.resolve('The Ricks Must Be Crazy')
]);
console.log(a + ' ' + b);
// Rejected promises will be handled by try/catch as if code was synchronous
const seasonTwoFinale = yield Promise.reject(new Error('Tammy'));
// Never executed
const seasonThree = 'Coming Soon';
} catch (error) { console.error(error.message); } // Tammy
})
@getify hey thanks for commenting! It was your blog post on ES6 generators that set me down this path and got me thinking about different kinds of generator applications in the first place! Really helpful stuff. Every other post seemed to be focused on simplistic linear number generators, you had the first article I found that explained the control-flow of a non-trivial generator.
I got a little too caught up on having as short and concise an implementation as possible with this so a lot of stuff got left out. Thanks for all the suggestions and improvements! I'm headed out for vacation today but I'm looking forward to working through your improvements when I get back.