JavaScript asynchronous programming Some solutions to "callback Hell"

Source: Internet
Author: User
Tags event listener readfile

Asynchronous programming is very important in JavaScript. Too many asynchronous programming have problems with callback nesting, and this article provides some ways to fix "callback Hell".

setTimeout(function () {    console.log(‘延时触发‘);}, 2000);fs.readFile(‘./sample.txt‘, ‘utf-8‘, function (err, res) { console.log(res);});

The above is a typical callback function, whether in the browser or in node, JavaScript itself is single-threaded, so in order to deal with some of the problems caused by single-threaded, asynchronous programming is a very important part of JavaScript.

Whether it is the most common in the browser Ajax, event monitoring, or node file read, network programming, database and other operations, can not be separated from asynchronous programming. In asynchronous programming, many operations are placed in the callback function (callback). A mix of synchronous and asynchronous, too many callback nesting will make the code difficult to understand and maintain, which is often criticized by people.

Let's look at the following code

fs.readFile(‘./sample.txt‘, ‘utf-8‘, (err, content) => {    let keyword = content.substring(0, 5); db.find(`select * from sample where kw = ${keyword}`, (err, res) => { get(`/sampleget?count=${res.length}`, data => { console.log(data); }); });});

First we read the keyword in a file keyword , and then according to the keyword database query, and finally request data based on the results of the query.

It contains three asynchronous operations:

    • File read: Fs.readfile
    • Database query: Db.find
    • HTTP request: Get

As you can see, we add a bit more nesting of callback functions without adding an asynchronous request, and the nesting of three async functions in this code has begun to make it difficult to read and maintain a piece of code that can be explicitly spoken.

Abstract this code will become the following:

asyncFunc1(opt, (...args1) => {    asyncFunc2(opt, (...args2) => {        asyncFunc3(opt, (...args3) => {            asyncFunc4(opt, (...args4) => {                // some operation            });        });    });});

A triangular indent area appears on the left, and too many callbacks let us fall into the "callback Hell". The next step is to introduce some methods to circumvent the callback hell.

First, Disassembly function

One important problem with callback nesting is that code is difficult to read and maintain. Because of the widespread, excessive indentation (nesting) can greatly affect the readability of the code. Based on this, you can make one of the simplest optimizations--to disassemble each step into a singlefunction

functionGetData (count) {Get (${count} ', data = {console.log (data); });} function queryDB (kw) {db.find ( ' select * from sample where kw = ${kw} ', ( Err, res) = {GetData (res.length);}); function readFile (filepath" {fs.readfile (filepath,  ' Utf-8 ', (err, content) = {let keyword = content.substring (0, 5); QueryDB ( keyword); });} ReadFile (/sample.txt ');           

As you can see, the code is much clearer by the way it was rewritten. This method is very simple and has some effect, but lacks generality.

Second, event release/monitor mode

If you write an event listener in addEventListener your browser, you must be familiar with the mode of posting/listening to this event.

Using this idea, on the one hand, we can listen to an event, when the event occurs, the corresponding callback operation, on the other hand, when certain operations are completed, the callback is triggered by publishing the event. This allows you to decouple the code that was originally bundled together.

Const EVENTS =Require' Events ');const eventemitter = new events. Eventemitter (); Eventemitter.on ( ' db ', (err, kw) = {Db.find (${kw} ', (err, res) = = {Eventemitter ( ' get ', res.length);}); Eventemitter.on ( ' get ', (err, count) = {get ( "/sampleget?) Count=${count} ', data = {console.log (data);}); Fs.readfile ( ' Utf-8 ', (err, content) = { Span class= "Hljs-keyword" >let keyword = content.substring (0, 5) ; Eventemitter. Emit ( ' db ', keyword);           

Implementations that use this pattern require a library of event publishing/listening. The above code uses the node native events module, of course you can use any library you like.

Third, Promise

Promiseis an asynchronous solution that was first proposed and implemented by the community and later written into the ES6 specification.

Currently, some mainstream browsers have natively implemented Promise APIs that can be used to view browser support in can I use. Of course, if you want to do browser compatibility, consider using some Promise of the implementation libraries, such as Bluebird, Q, and so on. Let's take Bluebird as an example:

First, we need to rewrite the Async method to Promise Use the Bluebird method for a callback function that conforms to the node specification (the first argument must be error) promisify . The method receives a standard asynchronous method and returns an Promise object.

const bluebird = require(‘bluebird‘);const fs = require("fs");const readFile = bluebird.promisify(fs.readFile);

In this way, readFile it becomes an Promise object.

However, some async methods cannot be converted, or we need to use native Promise , which requires some manual modification. Here is a way to retrofit.

fs.readFileas an example, use native Promise to transform the method:

const readFile = function (filepath) {let resolve, reject; let promise = new Promise ( (_resolve, _reject) = {resolve = _resolve; reject = _reject;}); let deferred = {resolve, Reject, promise}; Fs.readfile (filepath,  ' Utf-8 ', function (err, ... args" {if (err) {deferred.reject (err);} else {deferred.resolve (... args);}); return deferred.promise;}          

We create an object in the method Promise and use it to change the state of the object in the asynchronous callback according to different circumstances reject resolve Promise . The method returns the Promise object. Other asynchronous methods can also be modified in this way.

It is assumed that through the transformation, and readFile queryDB the getData method will return an Promise object. The code becomes:

readFile(‘./sample.txt‘).then(content => {    let keyword = content.substring(0, 5); return queryDB(keyword);}).then(res => { return getData(res.length);}).then(data => { console.log(data);}).catch(err => { console.warn(err);});

As you can see, the previous nested operation was programmed through then the chained operation of the connection. There is a big improvement in the cleanliness of the code.

4 generator
generatoris a new syntax in ES6. functionAdding a * After a keyword changes the function to a generator .

const gen = function* () {    yield 1; yield 2; return 3;}

Execution generator will return a Walker object that iterates through generator the internal state.

let g = gen();g.next(); // { value: 1, done: false }g.next(); // { value: 2, done: false }g.next(); // { value: 3, done: true }g.next(); // { value: undefined, done: true }

It can be seen that the generator function has one of the greatest characteristics, can be in the process of internal execution of the control of the program, yield equivalent to play a role in a pause, and when under certain circumstances, the external control over the transfer back.

Imagine that we used generator to encapsulate the code, use the keyword at the asynchronous task yield , generator give the program execution to the other code, and after the asynchronous task completes, invoke the next method to restore yield the execution of the code below. Take ReadFile as an example, the approximate process is as follows:

//our main task--display keyword // Use yield to temporarily interrupt the code below execution //yield followed by promise object const Showkeyword = Span class= "hljs-function" >function* (filepath) {console.log ( ' start reading '); let keyword = yield readFile (filepath); console.log (" keywords are ${filepath} ');} //generator Process Control let gen = Showkeyword (); let res = Gen.next (); Res.value.then (res = Gen.next (res))                

In the main task section, the original readFile asynchronous part becomes a similar synchronous notation, and the code becomes very clear. In the lower half, it is the process control that is given when a control is required to be handed back generator .

However, we need a manual control generator process that is more practical if it can be executed automatically- generator automatically handing over control when needed.

To do this, we can use the Co library. It can be a code that eliminates our generator control of the process

const co = reuqire ( ' co '); //our main task--display keyword //use yield to temporarily interrupt code execution below //yield followed by promise object const Showkeyword = function* (filepath) { console.log ( ' start reading '); let keyword = yield readFile (filepath); console.log (" keywords are ${filepath} ');} //using Coco (Showkeyword);           

Which, yeild after the keyword needs to be functio ,, promise generator , array or object . Can rewrite the first example of the article:

const co = reuqire(‘co‘);const task = function* (filepath) { let keyword = yield readFile(filepath); let count = yield queryDB(keyword); let data = yield getData(res.length); console.log(data);});co(task, ‘./sample.txt‘);
Wu, async/await

As you can see, the above method solves the problem of callback in asynchronous programming to some extent. However

    • Function splitting is actually just splitting blocks of code, which is often detrimental to subsequent maintenance;
    • The event publishing/Listening mode blurs the process relationship between asynchronous methods;
    • PromiseAlthough multiple nested asynchronous calls can be manipulated through a chained API, too much of then it increases the redundancy of the Code, and it has some interference with the asynchronous tasks in each phase of the code.
    • generatorAlthough it can provide a good grammatical structure, but after all, generator with yield the context of how much is not quite appropriate.

So here's another way to do this, which is the async/await in Es7.

A brief introduction to Async/await. Basically, any function can be an async function, and the following are all valid forms of writing:

async function foo () {};const foo = async function () {};const foo = async () => {};

asyncstatements can be used in functions await . awaitis usually an Promise object.

async function foo () { console.log(‘开始‘); let res = await post(data); console.log(`post已完成,结果为:${res}`);};

When the above function is executed, it await can be simply understood that the function hangs, waits for await the Promise return, and executes the following statement.

It is worth noting that the code for this asynchronous operation looks like a "synchronous operation." This greatly facilitates the writing and reading of asynchronous code. Let's rewrite our example below.

const printData = async function (filepath) { let keyword = await readFile(filepath); let count = await queryDB(keyword); let data = await getData(res.length); console.log(data);});printData(‘./sample.txt‘);

As you can see, the code is simple and clear, and the asynchronous code has the structure of the "synchronous" code.

Note that readFile both, queryDB and getData methods need to return an Promise object. This can be rewritten in the way that is provided in the third part Promise .

Postscript

Asynchronous programming, as part of JavaScript, has a very important position, which helps us avoid thread blocking caused by synchronous code, and also poses some difficulties for coding and reading. Too much callback nesting can easily get us into "callback hell" and make the code mess. To solve the "callback hell", we can use these five common methods as described in the article:

    • Function disassembly
    • Event Publish/Subscribe mode
    • Promise
    • Generator
    • Async/await

Understanding the principles and implementation of various methods, and understanding the pros and cons, can help us to better asynchronous programming.



Alienzhou
Links: HTTP://WWW.JIANSHU.COM/P/BC7B8D542DCD
Source: Pinterest

JavaScript asynchronous programming Some solutions to "callback Hell"

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.