The more you know, the more you don’t know

The reason for Promise

There was a time when we had to use a lot of callback functions to get the result of an asynchronous call. Let’s look at this example:

Get server data via Jquery ajax

let url1 = 'http://xxx.xxx.1';
$.ajax({
    url:url1,
    error:function (error) {},
    success:function (data1) {
        console.log(data1); }});Copy the code

When multiple asynchronous requests need to be sent, and each request needs to depend on each other, we can only solve the problem in a nested manner

let url1 = 'http://xxx.xxx.1';
let url2 = 'http://xxx.xxx.2';
let url3 = 'http://xxx.xxx.3';
$.ajax({
    url:url1,
    error:function (error) {},
    success:function (data1) {
        console.log(data1);
        $.ajax({
            url:url2,
            data:data1,
            error:function (error) {},
            success:function (data2) {
                console.log(data2);
                $.ajax({
                    url:url3,
                    data,
                    error:function (error) {},
                    success:function (data3) {
                        console.log(data3); }}); }}); }});Copy the code

When dealing with multiple asynchronous logic, you need multiple layers of nested callback functions, which is often referred to as callback hell.

The main problems with this encoding mode are:

  • Code bloat.
  • Poor readability.
  • The coupling degree is too high and the maintainability is poor.
  • Poor code reuse.
  • Bug prone.
  • Exceptions can only be handled in callbacks.

Promise was invented to solve the problems raised by callback functions

What is a Promise

Promise is a solution to asynchronous programming that is more reasonable and powerful than traditional asynchronous solutions [callback functions] and [events].

  • Syntactically, a promise is an object from which to get messages for asynchronous operations
  • In its original sense, it is a promise that will give you results over time

Code writing comparison

I’ll start by encapsulating an Ajax method that supports promises. If you don’t understand the code, I’ll walk you through the Promise implementation mechanism

function request(url,data = {}){
    return new Promise((resolve,reject) = >{
        $.ajax({
            url,
            data,
            success:function (data) {
                resolve(data);
            },
            error:function (error) { reject(error); }})}); }Copy the code

Implement the previous multiple interdependent network requests using the Request method

let url1 = 'http://xxx.xxx.1';
let url2 = 'http://xxx.xxx.2';
let url3 = 'http://xxx.xxx.3';
request(url1)
    .then((data) = >{
        console.log(data);
        return request(url2,data)
    })
    .then((data) = >{
        console.log(data);
        return request(url3,data)
    })
    .then((data) = >{
        console.log(data)
    })
    .catch((error) = >{
        console.log(error);
    });

Copy the code

The characteristics of Promise

The state of the promise

  • Pending
  • This is a big pity.
  • Rejected (= rejected)

Final value and rejection

  • Final value: the value passed to the resolution callback when the promise is resolved
  • Rejection reason: Rejection reason that is passed to the exception callback when a promise is rejected

The relationship between state and state, between state and final value and rejection

  • Pending can be moved to pity or Rejected
  • This song cannot be transferred to other states, and must have an immutable final value
  • Rejected cannot be migrated to another state and must have an immutable data cause

The use of the Promise

The constructor

Promise is a constructor that returns a Promise object using the new operator

The constructor takes an excutor function as an argument

The excutor function takes two arguments of type resolve and reject

let p = new Promise((resolve,reject) = >{
     // Perform asynchronous operations in the excutor function
     Resolve or reject is called when the asynchronous operation is complete
});
Copy the code
  • When the constructor is called, the excutor function is immediately executed as synchronized code
  • We usually perform our asynchronous operations in excutor functions
  • When resolve and REJECT are not called, the state of the Promise object is pending
let p1 = new Promise((resolve,reject) = >{
    setTimeout((a)= >{
        console.log('p1');
    },1000);
});
// the state of p1 is always pending
Copy the code
  • When resolve is called, the resolve argument is a non-Promise object, a non-Thenable object
    • The argument to the resolve function as the final value of the Promise object
    • The state of the promise object will become depressing
let p2 = new Promise((resolve,reject) = >{
    setTimeout((a)= >{
        console.log('p2');
        resolve('I'm the final value of p2.')},1000);
});
// the state of P2 is pending within 1000ms
// Code execution, 1000ms later, P2 will be a pity
// After 1000ms, the final value of P2 is' I am the final value of P2 '.
Copy the code
  • When resolve is called, the argument to resolve is a Promise object
    • The state, final value, and rejection of the promise object are synchronized with the incoming Promise object
let p = new Promise((resolve,reject) = >{
    reject('error')})let p1 = new Promise((resolve,reject) = >{
    resolve(p)
})
// p1 is in the rejected state
Copy the code
  • When resolve is called, resolve takes the thenable object
    • The Thenable object is expanded, and the state, end value, and rejection of the Promise object depend on the result of the thenable object’s then method call
let thenable1 = {
    then:function(resolve,reject){
        resolve(1)}}let thenable2 = {
    then:function(resolve,reject){
        reject(2)}}let thenable3 = {
    then:function(resolve,reject){
        throw new Error(3)}}let thenable4 = {
    then:function(fn1,fn2){
        // do not call fn1 fn2}}let p1 = new Promise((resolve,reject) = >{
    resolve(thenable1);
})
let p2 = new Promise((resolve,reject) = >{
    resolve(thenable2);
})
let p3 = new Promise((resolve,reject) = >{
    resolve(thenable3);
})
let p4 = new Promise((resolve,reject) = >{
    resolve(thenable4);
})
// The final value of P1 will be 1
// P2 is in the rejected state and the final value is 2
// p3 is in the rejected state because Error: 3
// P4 is in a pending state
Copy the code
  • When you call reject, the reject argument is used as a reject for the Promise object
  • The status of the Promise object changes to Rejected
let p3 = new Promise((resolve,reject) = >{
    setTimeout((a)= >{
        console.log('p3');
        reject('I'm a p3 rejection')},1000);
});
// the state of p3 is pending for 1000ms
// After 1000ms, p3 is in the rejected state
// after 1000ms, p3 reject because 'I am p3 reject because'
Copy the code

Method on the Promise object

Then method:

A promise provides a THEN method that accesses its final value and rejection.

Promise’s then method accepts two arguments:

promise.then(onFulfilled, onRejected);
Copy the code
  • The onFulfilled function is used to accept the final value when the promise state becomes fulfilled
  • The onRejected function is used to accept the rejected when the promise state changes to Rejected
new Promise((resolve,reject) = >{
    setTimeout((a)= >{
        resolve('Data acquired by asynchronous task')},50)
}).then((data) = >{
    console.log(data)
})
// Data obtained by asynchronous tasks
Copy the code
new Promise((resolve,reject) = >{
    setTimeout((a)= >{
        reject(new Error('Asynchronous task exception'))},50)
}).then(null,(error)=>{
    console.log(error)
})
// Error: The asynchronous task is abnormal
Copy the code
new Promise((resolve,reject) = >{
    throw new Error('Throw an exception');
}).then(null,(error)=>{
    console.log(error)
})
// Error: throws an exception
Copy the code
OnFulfilled and onRejected parameters are optional
  • If ondepressing is not a function, it must be ignored
  • If onRejected is not a function, it must be ignored
OnFulfilled features

If ondepressing is a function:

  • It must be called when the promise execution ends, and its first argument is the promise’s final value
  • It cannot be called until the promise execution ends
  • It cannot be invoked more than once
OnRejected features

If onRejected is a function:

  • When a promise is rejected it must be called, and its first argument is the promise’s justification
  • A promise cannot be invoked until it is rejected
  • It cannot be invoked more than once
OnFulfilled and onRejected call time
  • This is called when the state of the Promise object becomes fulfilled or Rejected
  • Ondepressing and onRejected are always asynchronous calls
  • Ondepressing and onRejected are processed as micro tasks in the event queue
console.log(1);
setTimeout(function(){
    console.log(2)},0)
new Promise((resolve,reject) = >{
    resolve(3);
}).then((data) = >{
    console.log(data);
})
console.log(4)
// print: 1 4 3 2
Copy the code
OnFulfilled and onRejected call requirements
  • OnFulfilled and onRejected must be called as functions
  • In non-strict mode, this is a global object
  • In strict mode, this is undefined
function fn1(){
    new Promise((resolve) = >{
        resolve();
    }).then(function(){
        console.log(this)})}function fn2(){
    "use strict";
    new Promise((resolve) = >{
        resolve();
    }).then(function(){
        console.log(this)
    })
}
fn1(); // print: window
fn2(); // print: undefined
Copy the code
Multiple calls to the then method
  • The then method can be called multiple times by the same Promise object
  • The then method returns a new Promise object
  • When the promise is successfully implemented, all ondepressing needs to be called back in sequence according to its registration order
  • When a promise is rejected, all onRejected are called back in the order in which they were registered
let p = new Promise((resolve) = >{
    resolve()
});
let p1 = p.then((a)= >{
    console.log('Asynchronous execution, the first ondepressing');
});
let p2 = p.then((a)= >{
    console.log('Asynchronous execution, the second ondepressing');
});
console.log(p1.constructor === Promise);
console.log(p === p1);
console.log(p === p2);
console.log(p1 === p2);
// print: true
// print: false
// print: false
// print: false
// print: asynchronous execution, the first ondepressing
// print: asynchronous execution, the second ondepressing
Copy the code
The return value of the then method

The then method returns a Promise object

promise2 = promise1.then(onFulfilled, onRejected);   
Copy the code
  • This is a big pity. If onFulfilled and onRejected return a non-promise object and a non-Thenable value X, then the promise2 state will be fulfilled and the final value will be X
let p = new Promise((resolve,reject) = >{
    throw new Error(a); });let p1 = p.then(null,(data)=>{
    return 'I'm the final value of p2.'
});
p1.then((data) = >{
    console.log(data)
});
// print: I am the final value of p2
Copy the code
  • This is a big pity. If onFulfilled and onRejected return a promise object (x), the state, final value and rejection of PROMISe2 will be synchronized with X
let p1 = new Promise((resolve,reject) = >{
    resolve(1)})let p2 = new Promise((resolve,reject) = >{
    reject(2)})let p3 = new Promise((resolve) = >{
    resolve()
})
let p4 = p3.then((a)= >{
    return p1;
})
let p5 = p3.then((a)= >{
    return p2;
})
// The final value of P4 will be fulfilled gradually
// p5 status is Rejected because 2
Copy the code
  • This is a big pity. If onFulfilled and onRejected return a Thenable object, the Thenable object will be implemented, and the state, final value and rejection of promise2 depend on the call result of the THenable object’s THEN method
let thenable1 = {
    then:function(resolve,reject){
        resolve(1)}}let thenable2 = {
    then:function(resolve,reject){
        reject(2)}}let p1 = new Promise((resolve,reject) = >{
    resolve()
})
let p2 = p1.then((a)= >{
    return thenable1;
})
let p3 = p1.then((a)= >{
    return thenable2;
})
// THE final value of P2 will be fulfilled
// p3 is in the rejected state because 2
Copy the code
  • This is a big pity. If onFulfilled or onRejected throws an exception (e), then the promise2 state is “Rejected”, because E
let p = new Promise((resolve,reject) = >{
    resolve();
});
let p1 = p.then((data) = >{
    throw new Error('error')}); p1.then(null,(err)=>{
    console.log(err);
});
// print:  Error: error
Copy the code
  • If ondepressing is not a function and promise1 performs successfully, the state of promise2 will be the final value of the inhibition
let p = new Promise((resolve,reject) = >{
    resolve(I'm the final value of P1.);
});
let p1 = p.then(null.null);
p1.then((data) = >{
    console.log(data);
});
// print:  I'm the final value of p1
Copy the code
  • If onRejected is not a function and promise1 rejects it, promise2 is in the rejected state because of the rejected cause of promise1
let p = new Promise((resolve,reject) = >{
    reject('I am the objection to P1');
});
let p1 = p.then(null.null);
p1.then(null,(err)=>{
    console.log(err);
});
// print
Copy the code
  • This is a big pity. If the onFulfilled and onRejected executes an exception, the promise2 state is Rejected
let p = new Promise((resolve,reject) = >{
    resolve('I am the final value of P');
});
let p1 = p.then((data) = >{
    throw new Error('abnormal')}); p1.then(null,(err)=>{
    console.log(err);
});
/ / print: Error: abnormalities
Copy the code
Penetration characteristics of final values and rejection factors
  • If the promise state becomes fulfilled, the then method does not register onFulfilled
    • The state of the Promise object returned by the then method becomes depressing
    • The then method returns the same final value of the promise object as the original Promise object
  • If the promise status changes to Rejected, the then method does not register onRejected
    • The state of the Promise object returned by the then method changes to Rejected
    • The rejection of the promise object returned by the then method is the same as that of the original Promise object
let p1 = new Promise((resolve,reject) = >{
    resolve(1)})let p2 = new Promise((resolve,reject) = >{
    reject(2)})let p3 = p1.then(null.null);
let p4 = p2.then(null.null);
// The state of P3 is the depressing final value 1
// P4 is in the rejected state


p5 = p3.then(null.null);
p6 = p4.then(null.null);
// The state of P3 is the depressing final value 1
// P4 is in the rejected state
Copy the code
  • The penetration feature is mainly used for exception handling
let fn1 = function(){}
let fn2 = function(){}
let fn3 = function(){}
let fn4 = function(){}
let fn5 = function(){}
let onError = function(){};
new Promise((resolve,reject) = >{
    setTimeout(function(){
        reject()
    })
})
.then(fn1)
.then(fn2)
.then(fn3)
.then(fn4)
.then(fn5)
.then(null,onError)
Copy the code

Fn1, fn2, fn3, fn4, fN5 can all fail. An exception may occur when receiving the onRejected function registered in the last then function

The catch:

The catch(fn) method is actually an alias for the THEN (NULL,fn) method, and the return value of the catch method and the exception that occurs in the catch method are the same as when the THEN method was called

new Promise((resolve,reject) = >{
    reject()
}).then(null.function(error){})/ / is equivalent to
new Promise((resolve,reject) = >{
    reject()
}).catch(function(error){})Copy the code

A static method for Promise

Promise.resolve

  • The promise.resolve method is used to transform existing data into a Promise object
    • If the entry parameter is a Promise object
      • The state, end value, and rejection of the returned Promise object are synchronized with the entry of the promise.resolve method
    • If the entry parameter is the thenable object
      • The Thenable object is expanded, and the state, final value, and rejection of the returned Promise object depend on the result of the thenable object’s then method call
    • If the entry parameter is a non-Promise non-Thenable object
      • The state of the returned Promise object is fulfilled
      • The final value of the returned Promise object is the entry to the promise.resolve method
let p = Promise.resolve(x)
/ / equivalent to the
let p = new Promise((resolve) = >{
    resolve(x)
})
Copy the code

Promise.reject

  • The promise. reject method is used to return a Promise object whose state is Rejected because the method entered it
let p = Promise.reject(x)
/ / equivalent to the
let p = new Promise((resolve,reject) = >{
    reject(x)
})
Copy the code

Promise.all

  • The promise.all method is used to wrap multiple Promise objects into a new Promise object
const p = Promise.all([p1, p2, p3]);
Copy the code
  • P1, P2, and P3 are all Promise objects. If they are not, call the promise. resolve method to convert them to a Promise object
  • The state of P is determined by P1, P2 and P3
    • This is a big pity. When P1, P2 and P3 will all become a pity
      • This is a big pity
      • The final values of P1, P2, and p3 form an array, which acts as the final value of P
    • When one of the states OF P1, P2 and P3 changes to Rejected
      • The status of P changes to Rejected
      • The first state changes to the rejected promise object as the rejected cause of P
let p1 = Promise.resolve(1);
let p2 = Promise.resolve(2);
let p3 = 3;

Promise.all([p1,p2,p3]).then((data) = >{
    console.log(data); / / print: [1, 2, 3]
})
Copy the code
let p1 = Promise.resolve(1);
let p2 = new Promise((resolve,reject) = >{
    setTimeout(function(){
        reject('p2 error')},1000)})let p3 = new Promise((resolve,reject) = >{
    setTimeout(function(){
        reject('p3 error')},500)})Promise.all([p1,p2,p3]).catch((error) = >{
    console.log(error); // print: p3 error
})
Copy the code

Promise.race

  • The promise.race method is also used to wrap multiple Promise objects into a new Promise object
const p = Promise.race([p1, p2, p3]);
Copy the code
  • P1, P2, and P3 are all Promise objects. If they are not, call the promise. resolve method to convert them to a Promise object
  • The state of P is determined by the promise object whose state in P1, P2 and P3 becomes the inhibition or Rejected first
  • The final value or rejection of p is determined by the first promise object to change its state
let p1 = Promise.resolve(1);
let p2 = new Promise((resolve,reject) = >{
    setTimeout(function(){
        reject('p2 error')},1000)})let p3 = new Promise((resolve,reject) = >{
    setTimeout(function(){
        reject('p3 error')},500)})Promise.race([p1,p2,p3]).then(data= >{
    console.log(data);
}).catch(error= >{
    console.log(error);
})
// print: 1
Copy the code
let p1 = new Promise((resolve,reject) = >{
    setTimeout(function(){
        resolve(1)},1000)})let p2 = new Promise((resolve,reject) = >{
    setTimeout(function(){
        reject('p2 error')},800)})let p3 = new Promise((resolve,reject) = >{
    setTimeout(function(){
        reject('p3 error')},500)})Promise.race([p1,p2,p3]).then(data= >{
    console.log(data);
}).catch(error= >{
    console.log(error);
})
// print: p3 error
Copy the code

Error capture for Promise

When the state of the promise is Rejected and the catch method is used on the Promise object, the exception information will be eaten by the Promise object

// In node environment
process.on('unhandledRejection', error => {
    console.log('unhandledRejection', error);
});
// In the browser
window.addEventListener('unhandledrejection',(e)=>{
    e.preventDefault();
    console.log(e);
});
Copy the code

The problem of Promise

  • You cannot cancel the Promise, nor can you stop waiting for the Promise without a state change
  • No then or catch methods, constructor (excutor function) error, cannot catch
  • In the unfinished state, you don’t know if you’ve just started or if you’re about to finish

Promise the topic

Subject to a

const promise = new Promise((resolve, reject) = > {
  console.log(1)
  resolve()
  console.log(2)
})
promise.then((a)= > {
  console.log(3)})console.log(4)
Copy the code

Results: 1, 2, 4, 3

Topic 2

const promise = new Promise((resolve, reject) = > {
  resolve('success1')
  reject('error')
  resolve('success2')
})

promise
  .then((data) = > {
    console.log(data)
  })
  .catch((err) = > {
    console.log(err)
  })
Copy the code

Results: success1

The title three

Promise.resolve(1)
  .then((data) = > {
    console.log(data)
    return 2
  })
  .catch((err) = > {
    return 3
  })
  .then((data) = > {
    console.log(data)
  })
Copy the code

Results: 1, 2

The title four

Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)
Copy the code

Results: 1.

Topic five

new Promise((resolve,reject) = >{
    console.log(3);
    let p = new Promise((resolve, reject) = >{
        console.log(7);
        setTimeout((a)= >{
           console.log(5);
           resolve(6); 
        },0)
        resolve(1);
    });
    resolve(2);
    p.then((arg) = >{
        console.log(arg);
    });

}).then((arg) = >{
    console.log(arg);
});
console.log(4);
Copy the code

Results: 3 7 4 1 2 5

If you have any questions about the above topics, you can interact with me in the message area

reference

  • Understand ES6 in depth
  • Introduction to ES6 standards

Series of articles recommended

  • ES6 series -let and const full parsing
  • ES6 series – Deconstruction assignment full analysis
  • ES6 series – Arrow functions fully resolved

Write in the last

  • If there are any mistakes in this article, please correct them in the comments section. If this article helped you, pleasegive a likeandFocus on
  • This article was published simultaneously withgithub, can be ingithubFind more articles, welcomeWatch & Star u
  • See the following article: Plan

Welcome to pay attention to the wechat public number [front-end small black house], every week 1-3 high-quality articles push, help you to progress