Talk about the evolution of JavaScript asynchronous functions

The Evolution of Async JavaScript describes The Evolution of JavaScript Asynchronous functions. First, Asynchronous through callback functions, then Promise/A+, generator functions, and in The future async functions. Thanks to Jingzhuang for his translation of this article. The content is as follows:

Let’s take a look at the evolution of JavaScript asynchronous functions over the years.

Callback function Callbacks

It seems like it should all start with callback functions.

Asynchronous JavaScript

As we know, asynchronous programming in JavaScript can only be done with first-class citizen functions in the JavaScript language: this means that we can take a function as an argument to another function, and inside that function we can call the function passed in (that is, the callback function). This is where callbacks come in: if you pass a function as an argument to another function (now called a higher-order function), you can call that function inside the function to do the job. Callback functions have no return value (do not attempt to use return) and are only used to perform certain actions within the function. Take an example: { if (err) { //error handling return; } Console. Log ('success');} Console. Log ('success'); });

In the example above, we demonstrated an error-first callbacks, which is one of the features of Node.js itself. All of the core modules in Node.js and most of the modules in the NPM repository will follow this feature when they are written.

The challenges of overusing callback functions:

Failure to organize your code properly can easily lead to callback hell, which makes your code hard to understand. It’s easy to leave out error-handling code.

You cannot use the return statement to return a value, and you cannot use the throw keyword.

It is for these reasons that the JavaScript world has been looking for viable solutions to make asynchronous JavaScript development easier.

One possible solution is the Async module. If you’ve been dealing with callbacks for a long time, you might get a good sense of how complicated it is to use asynchronous functions in JavaScript if you want to do something in parallel, or serialized, or even to map elements in an array using asynchronous functions. So, thank Caolan McMahon for writing the Async module to solve these problems.

With the Async module, you can easily write code in this way:[1, 2, 3], AsyncSquaringLibrary.square,  
 function(err, result){ 
 // result will be [1, 4, 9] 

Although Async module brings convenience to a certain extent, it is still not simple enough and the code is not easy to read. Therefore, Promise appeared.


The current JavaScript asynchronous standard dates back to 2012 and didn’t become available until ES6, however, the Promise term was not invented by the JavaScript community. The term comes from an article by Daniel P.Freedman published in 1976.

A Promise represents the end result of an asynchronous operation.

Now let’s use Promise to accomplish what the above code does. The promise-style code looks like this:  
 .then(function() { 
 .catch(function() { 
  //error handling 

You’ll notice that callbacks are also used in the Promise. A callback function is passed in both the then and catch methods, which are executed when a Promise is fulfilled and rejected, respectively. Another advantage of the Promise function is that it can be linked together to accomplish a series of tasks. For example, you can write code like this:


When you don’t have a ready-made Promise, you may need to tap into some Promise library. A popular option is bluebird. These libraries may provide more functionality than the native solution and are not limited to the features specified in the Promise/A+ standard.

But why don’t you use sugar methods? I suggest you read Promise: The Extension Problem first. For more information about Promise, refer to the Promise/A+ standard.

You might ask: If most libraries expose only the callback interface, how do I use Promise?

Well, that’s easy. The only thing you need to do is use a Promise to wrap the body of the function call that contains the callback. Such as:

The code for the callback style might look like this:

function saveToTheDb(value) { 
  db.values.insert(value, function (err, user) { 
    if (err) throw err; 
    // todo: insert user to db 

Now let’s change this to a code that supports promise-style calls:

function saveToTheDb(value) { return new Promise(function(resolve, reject) { db.values.insert(value, function(err, user) { // remember error first ;) if (err) { return reject(err); // don't forget to return here } resolve(user); }}})

There are already quite a few libraries or frameworks that support both, offering callbacks and Promise-style API interfaces. Now, if you want to provide a library, the best practice is to provide an interface to both. You can easily do this by:

function foo(cb) {  
 if (cb) { 
  return cb(); 
 return new Promise(function (resolve, reject) { 

Or, even simpler, you can start with promise-only interfaces and use tools like Callbackify to achieve backward-compatible goals. The Callbackify actually does something similar to the above code snippet, but USES a more generic approach to the implementation, and I recommend that you read the Callbackify source code.

Generator trailing/yield

JavaScript generators are a relatively new concept and a new feature of ES6 (also known as ES2015). Imagine a scenario like this:

When you’re executing a function, you can pause the function at some point, do some other work, and then return the function to continue, even with some new values, and continue. The scenario described above is exactly what the JavaScript generator functions are trying to solve. When we call a generator function, it does not execute immediately, but requires us to manually iterate (the next method). That is, you call the generator function, and it returns you an iterator. Iterators iterate over each breakpoint.

function* foo () { var index = 0; while (index < 2) { yield index++; // Pause function execution and execute the operation after yield}} var bar = foo(); // This returns an iterator console.log(; // { value: 0, done: false } console.log(; // { value: 1, done: false } console.log(; // { value: undefined, done: true }

Further, if you want to make it easier to write asynchronous JavaScript code using generator functions, you can use the co library, which is written by the famous TJ.

Co is a generator based process control tool for Node.js and browsers that promises a more elegant way to write non-blocking code. Using co, the previous example code, we can rewrite it using the following code:

co(function* (){  
}).then(function() { 
 // success 
.catch(function(err) { 
 //error handling 

You may ask: How do you implement parallel operations? The answer may be easier than you think. Here it is (it’s really just promise.all) :

yield [,];  
Async/ await

The concept of Async function was introduced in ES7 (which has not yet been formally standardized), and so far you can only convert it to ES5 code with a syntax converter like Babel if you want to use it. (Note: We are talking about the Async keyword, not the Async package in NPM).

In short, with the async keyword, you can easily do what you did before with the generator and CO functions. Except for hacks, of course.

You might ask, does having async keyword in ES7 make yield less important?

In fact, using yield to make things asynchronous is nothing more than a hack; yield means lazy sequences and iterators. Await perfectly separates the two points by first using yield for its original purpose and then using await to perform asynchronous operations.

Behind the scenes, async actually USES a Promise, which is why Async returns a Promise.

Therefore, we use the Async function to do something similar to what we did in the previous code. We can rewrite the code in this way:

async function save(Something) { try { await; Await completion of the code await await, similar to yield} catch (ex) {//error handling} console.log('success'). }

As you can see, with async, you need to put the async keyword at the beginning of the function declaration. After that, you can use the await keyword inside the function, which is similar to the yield effect.

Using async to complete parallel tasks is very similar to the way yiled does, except that at this point the provid.all is no longer implicit, you need to display the call to it:

async function save(Something) {  
  await Promise.all[,] 

Koa also supports async functions, so if you’re using Koa, you can now use this feature with Babel.

import koa from koa; let app = koa(); app.experimental = true; app.use(async function (){ this.body = await Promise.resolve('Hello Reader! ') }) app.listen(3000);

The above content to share the development of JavaScript asynchronous functions, I hope to help you.