JavaScript's Asynchronous Options in 2016

This post is about the next generation of asynchronous JavaScript tools and it assumes basic familiarity with promises and callbacks.

In its origin days, JavaScript used callbacks to manage asynchronous flows. Callbacks work but they tend to negatively impact readability. ES6 made Promises a language feature and they do a great job helping developers tame many types of asynchronous patterns. However, despite their many benefits, there is still room for improvement. JavaScript's upcoming asynchronous solutions attempt to make asynchronous code look more synchronous as well as handle streams of asynchronous events. Three of those upcoming tools are:

Generator functions

Observables

Async functions (async/await)

While each of these tools is its own complex topic, this post focuses on the high level functionality they bring to JavaScript.

Generators

Generator functions are an ES6 feature and currently have some browser support. They are supported by Babel and TypeScript if TypeScript is set to ES6 mode.

Generator functions are iterable iterators, which is a fancy way of saying that generator functions return a Generator object with a next() method and developers can use for of loops on Generators. Generator functions are not inherently asynchronous. Generator functions do however provide a mechanism for "pausing" a function.

Once yield is called, the generator function pauses. On the next call to next the generator resumes. Also notice that in this example the generator function actually concludes and done is set to true.

Also notice that the last value is undefined. If our generator function had returned a value it would have been in the last .next().value.

These are basic examples of how generators work. There is a lot more that can be done with them. Now that we've looked at the basics we'll examine how yield's pausing capability can be used to tame asynchronous code.

Using yield to Fake Synchronous Flow

The yield mechanism can be leveraged to fake synchronous style code. This is a relatively nuanced mechanism and is best left to a library like Q. Q features a spawn method which allows developers to run "asynchronous" generator functions.

Within our spawn function's generator callback we're able to write code that looks synchronous. This is a very handy feature and it's available in many browsers today. There are a lot of other things that can be done with generators, but for now let's take a look at some other asynchronous tools at our disposal.

Observables

Observables are a proposed language feature and exist today in the form of the RxJS 5 library. RxJS has been around for a while but with it being a proposed language feature, its increasing popularity and its similarity to generator functions it makes sense to include it here.

Observables are inherently asynchronous and they work somewhat like an inverted generator function. Like generator functions they embrace the idea of iteration. Observables do this by providing familiar Array functions like foreEach() and map(). Observables also come with a host of bells and whistles that make working with them quite pleasant.

formData is a simple observable that represents a form input box, specifically a search field

The search function returns an Observable that is supposed to represent the transformed results of an HTTP request

searchResults represents a series of array transforms to be run against the Observable

In searchResults we see that we are able to use filter to make sure we have "truthy" input coming from the form. If that check passes the form input is also debounced to ensure we don't overload our (fake) API. Then search is called.

The resulting Observable from search is unboxed with flatMap and its result which is an array is also unboxed with another flatMap.

Finally map is called once for each element of the result array. map performs a simple transform on the data.

The interesting part is that none of this happens until .subscribe() is run meaning the entire sequence of array operations can be conveniently subscribed to over, and over, and over.

Just Scratching The Surface

Observables offer a world of options and these examples only begin to address what observables can do. RxJS implements many more operations than those listed here. Additionally the site rxmarbles has wonderful visual examples of observables in action.

Async Functions

Async functions are a proposed language feature and can be used today with Babel. The spec is still subject to change so be careful. Node users may also be interested in node fibers which offer similar benefits but only in node

Async functions are like a "batteries included" version of the way generator functions can make asynchronous code look like synchronous code. They are possibly the simplest of the three tools compared in this post. Async functions take promises and "flatten" them out so that they look synchronous. With async functions promises can even be used in try/catch blocks!

What's important to note is that the function syncStyleGoodness is marked with the async keyword. Within the async function the keyword await is used to ostensibly wait for the promise to resolve putting its result directly into a variable which can then be used. The code is almost purely synchronous looking except that the async function implicitly returns a promise.

That's all there is to it. try/catch blocks work around await marked calls just like the would synchronous code. In the event an error is thrown or a rejected promise is not handled, the implicit async promise will reject.

Async functions are pleasantly simple and they allow developers to express asynchronous code in possible the most synchronous looking way.

Summary

JavaScript is maturing into a language with rich asynchronous support. There are many tools to choose from depending on use case. Simple, one time asynchronous calls can (and in many cases should) still be handled with callbacks or promises. Complex series of promises can be "flattened" out into synchronous looking asynchronous code with async functions. Streams of asynchronous actions can be tamed with generator functions or Observables.

With so many options, it can be tempting to want to use all of them - but this is usually unnecessary. Like anything else, take the time to learn how the tools work and then choose the right one for the right task. And always remember to have fun along the way!