preface
It is well known that Javascript is a “single-threaded” language, and we have to deal with asynchronous logic in real development, so asynchronous programming becomes very necessary. Asynchronous refers to the execution of A task, which is divided into two phases A and B. After the execution of phase A, another task needs to be performed before the execution of phase B is completed. There are several common ways of asynchronous programming: callback, Promise, Generator, and Async.
The callback function
Callback refers to a reference to a piece of executable code that is passed to other executing code by function passing parameters, and then returned to the main function after being called by the main function, as shown in the following example:
function add(a, b, callback){
var num = a + b;
callback(num)
}
add(1, 2, function(num){
console.log(num); # 3
#...
})
Copy the code
If you have a task queue that contains multiple tasks, then you need layers of nesting
var readFile = require('fs-readfile-promise'); # Read file function
readFile(fileA, function(data) {
readFile(fileB, function(data) {
#...})})Copy the code
If I have n tasks, then I need to nest n layers of code, so that the code is very redundant and cluttered and highly coupled, and if I change one of the functions, I will affect the logic of the code block of the next function. This is known as’ callback hell ‘
Promise
Promise is a common solution to the problem of asynchronous callbacks. Allows you to change the nesting of callback functions to chain calls. For more than one of the above tasks, you can transform it into the following example:
function add(a, b){
return new Promise((resolve, reject) => {
var result = a+b;
resolve(result);
})
}
add(10, 20).then(res => {
return add(res, 20) # res = 30
}).then(res => {
return add(res, 20) # res = 50}).then(res => { // ... }). Catch (err => {// error handling})Copy the code
The add function returns a Promise that goes into the then method, the first argument being the Promise’s resolve result and the second (optional) reject result of the Promise. We can write the logic after the callback in the THEN method. This chain writing method effectively separates the callback processing of each event, making the code structure clearer. In addition, we can handle the error in catch.
If the request is not A->B->C->D, but [A,B,C]->D, we can use promise.all ();
Generate an array of Promise objects
const promises = [2, 3, 5].map(function (id) {
return getJSON('/post/' + id + ".json"); # getJSON is a data request function that returns Promise wrapped data
});
Promise.all(promises).then(function (posts) {
# Promises contains three promises
# posts returns an array of three Promise returns
This is where you can perform the D task
}).then(res => {
//...
}).catch(function(reason){
//...
});
Copy the code
However, the Promise code still has some redundant code, such as a bunch of new Promise, then, and catch functions wrapped in Promise.
The Generator function
Generator function is an asynchronous programming solution provided by ES6. Each time the function is executed, it returns a traverser object, which can iterate over each state in the Generator in turn. We need to use the next method of the traverser object to execute the function.
Here’s an example:
function* foo() {
yield 'stepone';
yield 'steptwo';
return 'stepthree';
}
var _foo = foo();
_foo.next(); #{value: 'stepone', done: false}
_foo.next(); #{value: 'stepone', done: false}
_foo.next(); #{value: 'stepone', done: false}
Copy the code
Generator has three characteristics: functions are named with * after function; There’s a yield inside the function; External execution requires calling the next method. Each yield wraps the value that follows it into the return of an object containing both the returned value and the state of the function until return, which returns done as true.
If we need to use next every time we run Generator functions, we need an auto-executor. The CO module is a small tool released in June 2013 by TJ Holowaychuk, a well-known programmer, for automatic execution of Generator functions. When the CO module is used, yield can only be a Thunk function or a Promise object. When the CO function completes, it returns a Promise. As follows:
var co = require('co');
var gen = function* () {
var img1 = yield getImage('/image01');
var img2 = yield getImage('/image02'); . }; co(gen).then(function (res){
console.log(res);
}).catch(err){
# error handling
};
Copy the code
Parallel processing of the tasks of the CO module, and the next operation will be carried out after the parallel execution of multiple tasks:
# array
co(function* () {
var res = yield [
Promise.resolve(1),
Promise.resolve(2)
];
console.log(res);
}).then(console.log).catch(onerror);
How to write the object
co(function* () {
var res = yield {
1: Promise.resolve(1),
2: Promise.resolve(2),
};
console.log(res);
}).then(console.log).catch(onerror);
Copy the code
Generator functions are more concise and logical than Promise, but they need to be executed by an additional CO function. In order to solve the problem of optimization, async functions appear.
Async function
Async functions are syntactic sugar for Generator functions.
var co = require('co');
var gen = function* () {
var img1 = yield getImage('/image01');
var img2 = yield getImage('/image02'); . }; co(gen).then(function (res){
console.log(res);
}).catch(err){
# error handling}; * * * *# The above Generator function can be changed
var gen = async function () {
var img1 = await getImage('/image01');
var img2 = await getImage('/image02');
return[img1, img2]; . }; gen().then(res => { console.log(res)# [img1, img2]
});
Copy the code
Compared with Generator functions, async functions are written differently in that async replaces *, await replaces yield, and async comes with an executor that only gen() is needed to execute the function. With good adaptability, await can be followed by Promise or primitive type value; In addition async functions return promises so that we can better handle the returned value.
async function gen() {
return '111';
# equivalent to return await '111';
};
gen().then(res => {
console.log(res) # 111
});
Copy the code
If it is a direct return value, it automatically becomes the value in the then method callback function.
async function gen() {
var a = await getA();
var b = await getB();
return a + b;
};
gen().then(res => {
console.log(res)
});
Copy the code
A Promise returned by an async function cannot change its state until all await Promise objects behind an await in the function have been executed, or return or throw an error. Async: async: async: async: asynC: asynC: asynC: asynC: asynC
# Error handling 1
async function gen() {
await new Promise((resolve, reject) => {
throw new Error('Wrong'); })}; gen().then(res => { console.log(res) }).catch(err => { console.log(err)# make a mistake
});
Error handling 2: An error in an await task will not affect the execution of an await task
async function gen() {
try{
await new Promise((resolve, reject) => {
throw new Error('Wrong');
})
}catch(e){
console.log(e); # make a mistake
}
return Promise.resolve(1);
};
gen().then(res => {
console.log(res) # 1
});
Copy the code
Error handling as above.
async function gen() {
# write a
let result = await Promise.all([getName(), getAddress()]);
return result;
# write two
let namePromise = getName();
let addressPromise = getAddress();
let name = await namePromise;
let address = await addressPromise;
return [name, address];
};
gen().then(res => {
console.log(res); An array of getName and getAddress returns, respectively
})
Copy the code
If multiple asynchronous tasks do not depend on each other and require concurrent writing, use the preceding two methods.
Contrast async with Promise and Generator functions
function chainAnimationsPromise(elem, animations) {
The # ret variable is used to hold the return value of the previous animation
let ret = null;
Create an empty Promise
let p = Promise.resolve();
Add all animations using the then method
for(let anim of animations) {
p = p.then(function(val) {
ret = val;
return anim(elem);
});
}
# return a Promise with error catching deployed
return p.catch(function(e) {
# error handling
}).then(function() {
return ret;
});
}
Copy the code
Promise solves the problem of hell callback well, but there are many semantically irrelevant THEN and catch in the code.
function chainAnimationsGenerator(elem, animations) {
return co(function* () {let ret = null;
try {
for(let anim of animations) {
ret = yield anim(elem);
}
} catch(e) {
# error handling
}
return ret;
});
}
Copy the code
Generator functions require an autoexecutor to execute the function, and yield can only be followed by a Promise object or a Thunk function.
async function chainAnimationsAsync(elem, animations) {
let ret = null;
try {
for(let anim of animations) {
ret = await anim(elem);
}
} catch(e) {
# error handling
}
return ret;
}
Copy the code
The implementation of async function is the simplest and most semantic, with almost no semantically irrelevant code. In contrast to Generator, async does not require the programmer to provide an executor. Async executes itself automatically and is easy and concise to use.
Reference: ECMAScript 6 Ruan Yifeng