Use Async functions to simplify asynchronous code (JavaScript development skills)

Source: Internet
Author: User
Tags function examples
Promise became popular on the Internet at the beginning of its release of JavaScript-they helped developers get rid of callback hell and solved the asynchronous problem that plagued JavaScript developers in many places. But Promises is far from perfect. They keep requesting callbacks, and there will still be some clutter and some incredible redundancy on some complex issues. Promise became popular on the Internet at the beginning of its release of JavaScript-they helped developers get rid of callback hell and solved the asynchronous problem that plagued JavaScript developers in many places. But Promises is far from perfect. They keep requesting callbacks, and there will still be some clutter and some incredible redundancy on some complex issues.

With the advent of ES6 (now known as ES2015), in addition to introducing Promise specifications and having no need to request the countless libraries, we also have a generator. The generator can stop execution within the function, which means that they can be encapsulated in a multi-purpose function. We can wait until the asynchronous operation is completed before the Code moves to the next line. Suddenly, your asynchronous code may start to look synchronous.

This is only the first step. Asynchronous functions have been standardized since they joined ES2017 this year, and local support has been further optimized. Asynchronous functions use generators for asynchronous programming and give their own semantics and syntax. Therefore, you do not need to use a library to obtain encapsulated practical functions, because these functions are processed in the background.

To run the async/await instance in the article, you need a compatible browser.

Running compatibility

On the client side, Chrome, Firefox, and Opera can well support asynchronous functions.

From version 7.6, Node. js enables async/await by default.

Comparison between asynchronous functions and generators

There is an example of asynchronous programming using the generator, using the Q Library:

var doAsyncOp = Q.async(function* () {    var val = yield asynchronousOperation();    console.log(val);    return val;  });

Q. async is an encapsulation function that handles things after the scenario. * Indicates the function as a generator function, and yield indicates stopping the function and replacing it with an encapsulated function. Q. async will return a function. You can assign a value to it, just like doAsyncOp, and then call it.

The new syntax in ES7 is more concise. The operation example is as follows:

async function doAsyncOp () {    var val = await asynchronousOperation();         console.log(val);    return val;  };

The difference is not big. We deleted an encapsulated function and * symbol and replaced it with the async keyword. The yield keyword is also replaced by await. The two examples actually do the same thing: assign a value to val after asynchronousOperation is complete, then output and return the result.

Convert Promises to asynchronous Functions

If we use Vanilla Promises, what will the previous example look like?

function doAsyncOp () {    return asynchronousOperation().then(function(val) {      console.log(val);      return val;    });  };

There are the same number of lines of code, but this is because then and the callback function passed to it add a lot of additional code. Another annoying thing is two return keywords. This has always plagued me because it is difficult to figure out exactly what is returned using the promises function.

As you can see, this function returns a promises and will assign a value to val. Guess what the generator and asynchronous function examples have done! No matter what you return in this function, you actually secretly return the value parsed by a promise. If you do not return any value at all, the promise you secretly return is parsed as undefined.

Chain Operation

One of the reasons why Promise is popular is that it can connect multiple asynchronous operations in the form of chained calls, avoiding embedded callbacks. However, the async function is even better at this aspect than Promise.

The following demonstrates how to use Promise to perform chained operations (we just run asynchronousOperation multiple times for demonstration ).

function doAsyncOp() {    return asynchronousOperation()      .then(function(val) {        return asynchronousOperation(val);      })      .then(function(val) {        return asynchronousOperation(val);      })      .then(function(val) {        return asynchronousOperation(val);      });  }

To use the async function, you only need to call asynchronousOperation like writing the synchronization code:

async function doAsyncOp () {    var val = await asynchronousOperation();    val = await asynchronousOperation(val);    val = await asynchronousOperation(val);    return await asynchronousOperation(val);  };

Even the final return statement does not require await, because or not, it returns Promise containing the final value that can be processed.

Concurrent operations

Promise also has another great feature, which can perform multiple asynchronous operations at the same time and continue with other events after they are all done. The ES2015 specification provides Promise. all (), which is used to do this.

Here is an example:

function doAsyncOp() {    return Promise.all([      asynchronousOperation(),      asynchronousOperation()    ]).then(function(vals) {      vals.forEach(console.log);      return vals;    });  }

Promise. all () can also be used as an async function:

async function doAsyncOp() {    var vals = await Promise.all([      asynchronousOperation(),      asynchronousOperation()    ]);    vals.forEach(console.log.bind(console));    return vals;  }

Even if Promise. all is used, the code is still clear.

Rejected

Promises can be accepted (resovled) or rejected (rejected ). The rejected Promise can be processed through a function, which is passed to then as the second parameter or to the catch method. Now we have not used the method in Promise API. What should we do to reject it? Try and catch can be used for processing. When the async function is used, it is rejected as an error, so that they can be handled through the error processing code supported by JavaScript itself.

function doAsyncOp() {    return asynchronousOperation()      .then(function(val) {        return asynchronousOperation(val);      })      .then(function(val) {        return asynchronousOperation(val);      })      .catch(function(err) {        console.error(err);      });  }

This is very similar to the example of our chain processing, but changes the last part of it to call catch. If you use the async function for writing, it will be like the following.

async function doAsyncOp () {    try {      var val = await asynchronousOperation();      val = await asynchronousOperation(val);      return await asynchronousOperation(val);    } catch (err) {      console.err(err);    }  };

It is not as concise as other conversions to async functions, but it is indeed the same as writing synchronization code. If you do not capture errors here, it will extend the call chain and throw it up until it is captured and processed somewhere. If it has not been captured, it will eventually stop the program and throw a runtime error. Promise operates in the same way, but refuse not to handle it as an error; they may be a string that describes the error. If you do not capture a rejection that is created as an error, you will see a runtime error. However, if you only use a string, it will fail but no output will be generated.

Interrupt Promise

To reject native Promise, you only need to use Promise to build the reject in the function. Of course, you can also directly throw an error-in the Promise constructor, you can throw it in the then or catch callback. If an error is thrown elsewhere, Promise cannot handle it.

Here are some examples of rejecting Promise:

function doAsyncOp() {    return new Promise(function(resolve, reject) {      if (somethingIsBad) {        reject("something is bad");      }      resolve("nothing is bad");    });  }     /*-- or --*/     function doAsyncOp() {    return new Promise(function(resolve, reject) {      if (somethingIsBad) {        reject(new Error("something is bad"));      }      resolve("nothing is bad");    });  }     /*-- or --*/     function doAsyncOp() {    return new Promise(function(resolve, reject) {      if (somethingIsBad) {        throw new Error("something is bad");      }      resolve("nothing is bad");    });  }

In general, it is best to use new Error because it contains other information related to the Error, such as the row number of the throw position and the call stack that may be useful.

Here are some examples of errors that cannot be captured when a Promise is thrown:

function doAsyncOp() {    // the next line will kill execution    throw new Error("something is bad");    return new Promise(function(resolve, reject) {      if (somethingIsBad) {        throw new Error("something is bad");      }      resolve("nothing is bad");    });  }     // assume `doAsyncOp` does not have the killing error  function x() {    var val = doAsyncOp().then(function() {      // this one will work just fine      throw new Error("I just think an error should be here");    });    // this one will kill execution    throw new Error("The more errors, the merrier");    return val;  }

If an error is thrown in the Promise of the async function, the related range will not occur. You can throw an error in the async function anytime, anywhere, and it will always be caught by Promise:

async function doAsyncOp() {    // the next line is fine    throw new Error("something is bad");    if (somethingIsBad) {      // this one is good too      throw new Error("something is bad");    }    return "nothing is bad";  }      // assume `doAsyncOp` does not have the killing error  async function x() {    var val = await doAsyncOp();    // this one will work just fine    throw new Error("I just think an error should be here");    return val;  }

Of course, we will never run the second error in doAsyncOp, nor the return statement, because the previous error has aborted function execution.

Problem

If you are using the async function at the beginning, you need to be careful about nested functions. For example, if your async function has another function (usually callback), you may think that await can be used in it, but it actually cannot. You can only use await directly in the async function.

For example, this code cannot run:

async function getAllFiles(fileNames) {    return Promise.all(      fileNames.map(function(fileName) {        var file = await getFileAsync(fileName);        return parse(file);      })    );  }

The await of row 4th is invalid because it is used in a common function. However, you can solve this problem by adding the async keyword to the callback function.

async function getAllFiles(fileNames) {    return Promise.all(      fileNames.map(async function(fileName) {        var file = await getFileAsync(fileName);        return parse(file);      })    );  }

You will take it for granted when you see it. Even so, you still need to be careful about this situation.

You may also want to know the equivalent code for using Promise:

function getAllFiles(fileNames) {    return Promise.all(      fileNames.map(function(fileName) {        return getFileAsync(fileName).then(function(file) {          return parse(file);        });      })    );  }

The next question is to regard the async function as a synchronous function. Remember that the code inside the async function is synchronously run, But it immediately returns a Promise and continues to run the code outside, for example:

var a = doAsyncOp(); // one of the working ones from earlier  console.log(a);  a.then(function() {    console.log("`a` finished");  });  console.log("hello");     /* -- will output -- */  Promise Object  hello  `a` finished

You will see that the async function actually uses the built-in Promise. This allows us to think about the synchronization behavior in the async function. Others can call our async functions through the common Promise API, or use their own async functions.

Now, better asynchronous code!

Even if you cannot use Asynchronous code, you can compile it or compile it as es5. Asynchronous functions make the code easier to read and maintain. As long as we have source maps, we can use the ES2017 code more cleanly at any time.

There are many tools that can compile asynchronous functions (and other ES2015 + functions) into ES5 code. If you are using Babel, this is only an example of installing ES2017 preset.

The above describes how to use the Async function to simplify asynchronous code (JavaScript development skills). For more information, see other related articles in the first PHP community!

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.