Promise: A new solution for asynchronous programming introduced in ES6.
Promises are mainly to solve the previous problem of callback hell. Promise is a constructor that encapsulates an asynchronous operation and can get its success or failure result.
Promise accepts a function parameter, this function parameter has two formal parameters, the first is success (resolved by default), and the second is failure (reject by default). When calling
resolve()
Promise takes the success path, callingreject()
takes the failure path.1
2
3
4
5
6
7
8
9
10
11//Instantiate a Promise object
const p = new Promise(function(resolve, reject){ //resolve & reject. It's just an unspoken rule, you can name it yourself
setTimeout(function(){
let data = 'obj in database';
if(Math.random()>0.5){
resolve(data); //custom 'success'
}else{
reject(data); //custom 'failure'
}
}, 1000);
});
After success or failure, the
then()
method of thePromise
object can be called. Thethen()
method accepts two function-type parameters. Both function parameters have formal parameters. The successful formal parameter is generally calledvalue
, and the failed formal parameter is generally calledreason
.1
p.then(function(value){}, function(reason){})
The first function argument in
then()
is called when the Promise status is fulfilled. wherevalue
is the parameter of the previousresolve()
.1
2
3p.then(function(value){
console.log(value) //The value value is the parameter passed in by resolve() in the previous Promise
}, function(reason){})When the Promise status is rejected, the second function argument in
then()
is called. where thereason
value is the parameter of the previousreject()
.1
2
3
4p.then(function(value){
},function(reason){
console.log(reason) //The reason value is the parameter passed in by reject() in the previous Promise.
})
Promise encapsulates an example of an Ajax request.
Ajax request for JS in ES5
1
2
3
4
5
6
7
8
9
10
11
12const xhr = new XMLHttpRequest(); //The first step: create an object
xhr.open("GET","http://api.apiopen.top/getJoke"); //Part 2: Initialization
xhr.send(); //The third step: send
xhr.onreadystatechange = function(){ //Step 4: Bind events and process response results
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
console.log(xhr.response); //Processing successful results
}else{
console.error(xhr.status); //Process the result of failure
}
}
}Encapsulating Ajax with Promises
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21const p = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET","http://api.apiopen.top/getJoke");
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
resolve(xhr.response); //Declare success first, and pass parameters to then() before processing
}else{
reject(xhr.status); //Declare failure first, and pass parameters to then() before processing
}
}
}
})
// Specify success/failure callback
p.then(function(value){
console.log(value) //Success, pass in the parameter xhr.response of resolve() as the actual parameter of the formal parameter value.
},function(reason){
console.err(reason) //If it fails, pass in the parameter xhr.status of reject() as the actual parameter of the formal parameter reason.
})This example shows that Promises handle asynchronous success/failure results differently than in ES5. It turns out that ES5 operates in the callback function, and now the callback is specified by the
then()
method after the asynchronous task. This is clearly structured and doesn’t create callback hell problems.
then()
functionPromise
instances have athen()
method, that is, thethen()
method is defined on the prototype objectPromise.prototype
. Parameter 2 is optional. The return result ofthen()
is also aPromise
object.1
2
3
4
5
6
7
8
9
10
11
12
13const p = new Promise((resolve, reject) => {
setTimeout(() => {
reject('ERROR!')
}, 1000)
})
const result = p.then(value => {
console.log(value)
}, reason => {
console.warn(reason)
})
console.log(result); //The result is a Promise object, and the result is resolved/fulfilled (new version)
The return result of the
then()
method The state of the Promise object is determined by the execution result of the callback function.If the result returned in the callback function is non
promise
type data, status is success, and the return value is the success value of the object.1
2
3
4
5
6
7
8const result = p.then(value => {
console.log(value)
return 'success!';
}, reason => {
console.warn(reason)
})
console.log(result); //The status of the result is 'resolved' and the value is 'success!'.When no
return
is written, the default return result inside the function isundefined
.undefined
is not a Promise type object, so the return status result is also success.1
2
3
4
5
6
7
8const result = p.then(value => {
console.log(value)
//return 'success!'; no return
}, reason => {
console.warn(reason)
})
console.log(result); //The status of the result is 'resolved' and the value is undefined.
If the result returned in the callback function is a
promise
object, the state returned by the internalpromise
object determines the return of thethen()
method The state of the promise, and the value of the internal promise is the value of thepromise
returned by thethen()
method.1
2
3
4
5
6
7
8
9
10const result = p.then(value => {
console.log(value)
return new Promise((resolve, reject) => { //The return status of this promise determines the status of the promise returned by the then() method.
resolve('ok');
})
}, reason => {
console.warn(reason)
})
console.log(result); //The status of the result is 'resolved' and the value is 'ok'.1
2
3
4
5
6
7
8
9const result = p.then(value => {
console.log(value)
return new Promise((resolve, reject) => { //reject, similarly
reject('error');
}, reason => {
console.warn(reason)
})
console.log(result); //The status of the result is 'rejected' and the value is error.
If an error is thrown,
then()
returns apromise
with a status ofrejected
andvalue
as the value of the thrown error. Note thatthrow new Error('test');
andreject(new Error('test'));
are equivalent.1
2
3
4
5
6
7
8
9const result = p.then(value => {
console.log(value)
throw new Error('Error diagnosis') //Throwing an error, there is a throw, such as throw 'ERROR'; also acceptable.
})
}, reason => {
console.warn(reason)
})
console.log(result); //The status of the result is 'rejected' and the value is 'Error diagnosis'.
The chained invocation of the
then()
method, and the reason parameter can be omitted1
2
3
4p.then(value => {
}).then(value => {
}).then(value => {
})Callback hell can be avoided by chaining calls.
Chain call example: Each call passes in new data and saves all data on the chain.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22const p = new Promise((resolve, reject) => { //The first promise on the chain
fs.readFile("./resources/file1.md", (err, data) => { //node.js syntax
resolve(data);
});
});
p.then(value => { //The 2nd promise on the chain
return new Promise((resolve, reject) => {
fs.readFile("./resources/file2.md", (err, data) => {
resolve([value, data]); //The value is the data of file1, and the data is the data of file2.
});
});
}).then(value => { //The 3rd promise on the chain。Note that value here is the resolve value of the second promise on the chain:[value, data], which is the data of file1 and file2.
return new Promise((resolve, reject) => {
fs.readFile("./resources/file3.md", (err, data) => {
value.push(data);
resolve(value);
});
});
}).then(value => { //The value value is the content (array of) of the first three files
console.log(value.join('\r\n')); //combine
})
Break the Promise chain
In promise chains without
catch()
, eachthen()
on the chain has two function arguments, success and failure. But note that whether it succeeds or fails (return non-Promise, return a successful Promise, throw an error), it will triggerthen()
on the next chain.If you want to end the chain call directly after a
then()
in the chain fails to trigger, you need to call a Promise instance with a initialized state in thethen()
failure function.1
return new Promise(() => {});
catch()
Promise.prototype.catch()
is syntactic sugar for.then(null, rejection)
,catch()
method is used to specify the callback ofPromise
failure, which can uniformly handle errors on the Promise chain. Even ifcatch()
is used, allthen()
s in the chain do not need to write failure callbacks.1
2
3
4
5const p = new Promise((resolve, reject) => {
setTimeout(() => {
reject("Error!");
}, 1000)
});1
2
3p.then(function(value){}, function(reason){ //Method 1: Specify the failed callback through then()
console.error(reason)
})1
2
3p.catch(function(reason){ //Method 2: Specify the failure callback through catch(). Use the first value argument to write then().
console.error(reason)
})
Micro task, Macro task
The code in
new Promise()
, except asynchronous code andresolve
, will be executed directly in synchronous order. Andresolve
will be put into the micro task queue, so it will be executed after the synchronous code is executed.The code in
promise.then()
is asynchronous and placed in the micro task queue. So the execution order is after the synchronous code.And promise only has three states:
pending
,fulfilled
,rejected
. Therefore, as long asresolve
orreject
is encountered, the state will be condensed, and the followingfulfilled
,rejected
, if any, will not be executed. But if there is other code behind theresolve
orreject
code, such asconsole.log()
, then this part of the code will continue to be executed synchronously.If the promise is
pending
, neither.then()
nor.catch()
will execute.
If there are two asynchronous tasks, one is a micro task such as promise, and the other is a macro task such as setTimeout, then the micro will be executed first, and then the macro will be executed. Therefore, the code execution order is, synchronization > micro task > macro task. At the same time, after each macro task or micro task is executed, the system will recheck the macro task queue and macro task queue. Therefore, newly generated tasks with high priority will be executed first.