Author: Hancao wechat: HancAO97 introduction: a different north drift programmer, welcome to add wechat criticism and correction of communication technology

Writing background

In fact, the article I originally wanted to write was not Promise, but to summarize some knowledge related to Axios. However, there must be some basis to write the article. If I want to speak about Axios well, I must know its pre-knowledge, so I think I can make it into a series. Let’s move on to Axios’s plan. In fact, I believe that we do front-end development must be no stranger to Promise, so you might as well follow me to Promise this basic knowledge review. This article will include:

  • Promiseintroduce
  • PromiseThe characteristics of
  • Promiseuse
  • According to the usage and characteristics of the preceding text, write by handPromise

Without further ado, let’s get right to it!

Promise to introduce

Promise origin and purpose

  • PromiseFirst proposed and implemented in the community, the language standard was written in ES6.

ES6 is the sixth edition of ECMA-262. This edition contains some of the most important enhancements to the specification ever. Javascript is the implementation of ecMA-262

  • PromiseIs an asynchronous programming solution that is more rational, more powerful and more elegant than the traditional callback solution.
  • Syntactically: Use the Promise constructor to encapsulate an asynchronous operation to generate a Promise instance
  • What it does: Promise objects are used to encapsulate an asynchronous operation and provide a uniform API so that various asynchronous operations can be handled in the same way

Common asynchronous programming scenarios

  • Fs file operation
require('fs').readFile('./index.html'.(err,data) = >{
    // The callback function
})
Copy the code
  • Ajax operation
$.get('/api/getUser'.(data) = >{
    //handleData();
})
Copy the code
  • The timer
setTimeout(() = > {
    console.log('timeout');
},1000);
Copy the code

Why use Promise

  • Support for chained calls, which express asynchronous operations as a process of synchronous operations, can solve the callback hell problem

What is callback hell? Callbacks are nested, and the result of asynchronous execution of external callbacks is the execution condition of nested callbacks

// Callback to the typical hell scenario
asyncFunc1(opt,(. args1) = > {
    asyncFunc2(opt,(. args2) = > {
        asyncFunc3(opt,(. args3) = > {
            asyncFunc4(opt,(. args4) = > {
                //TODO: some opt})})})})Copy the code

Disadvantages of callback hell: it’s not easy to read, it’s not easy to handle exceptions, and it’s not pleasant

  • The way to specify callbacks is more flexible

Traditional: you must specify a Promise before starting an asynchronous task: you can monitor the status of an asynchronous task at any time, specify a callback function at any time, one or more.

  • Promise provides a uniform API that makes it easier to control asynchronous operations.

Which apis are provided will be explained in more detail in subsequent uses

Characteristics of Promise

The characteristics of Promise

  • The object state is not affected by the outside world

A Promise object represents an asynchronous operation. It has three states: pending, fulfilled, and rejected. The state can be changed only when the asynchronous operation is completed. The state is stored in the [[PromiseState]] property of the Promise object.

// Promise has two attributes:
let promiseA = new Promise((resolve,reject) = >{resolve(); })// The status corresponds to the [[PromiseState]] field of promiseA.
let promiseB = new Promise((resolve,reject) = >{resolve(1111); })// [[PromiseResult]] has the value 1111. This field is used to store the resolve(val) or reject(val) parameter [val].
Copy the code

For that reason, think about what a romantic name that is.

  • Once the state changes, there is no change

The state of a Promise object can change from Pending to Resolved and from Pending to Rejected. So as long as these two things happen, it’s frozen, it’s not going to change anymore, it’s going to stay that way. If you add a callback to a Promise object even after the change has occurred, you get this result immediately. This is completely different from events, which have the characteristic that if you miss it and listen again, you don’t get the result.

The shortcoming of Promise

  • Once created, it is executed immediately and cannot be canceled midway
  • PromiseAn error thrown internally cannot be reflected externally
  • pendingThere’s no way to know where you’re at

Promise to use

Promise instance creation

const promise = new Promise((resolve, reject) = > {
  if (/* Asynchronous operation succeeded */){
    resolve(value);
  } else{ reject(error); }});Copy the code

The Promise constructor takes a function as an argument, which is resolve and Reject. They are two functions that are provided by the JavaScript engine and do not have to be deployed.

  • The resolve function: changes the Promise object’s state from “incomplete” to “successful” (that is, frompendingintofulfilled), which is called when the asynchronous operation succeeds and the result of the asynchronous operation is passed as a parameter.
  • Reject: Changes the state of a Promise object from “unfinished” to “failed” (i.e. frompendingintorejected), which is called when the asynchronous operation fails and passes the error as a parameter.

Promise.prototype.then

After a Promise instance is generated, callbacks that specify the fulfilled and Rejected states can be used by the THEN method to eventually return a new Promise object

The callback function in tip: then is optional and does not have to be provided

promise.then(function(value) {
  This is very depressing
}, function(error) {
  // The rejected state is treated
});
Copy the code

Promise.prototype.catch

The catch callback function that handles the rejected state eventually returns a new Promise object

promise.catch((err) = >{
    handleReject();
    //
})
Copy the code

Promise.resolve

Return a Promise object for success or failure

let promiseA = Promise.resolve(1);
// If a non-Promise object is passed, the result is a successful Promise object
let PromiseB = Promise.resolve(new Promise((resolve,reject) = >{
    reject('err');
})
// If the parameter is a Promise object, the result returned by the parameter Promise is the result returned by promise.resolve
// For example, return a Promise object whose value is "err"
Copy the code

Promise.reject

Return a failed Promise object

let PromiseA = Promise.reject(new Promise((resolve,reject) = >{
    resolve('err');
})
// Return a failed Promise object, [[PromiseResult]] with the value of the promise.reject argument
Copy the code

Promise.all

The parameters received are an array of N Promise objects.

Tips: The return result is a new Promise, which succeeds only if all promise objects succeed, and fails only if one of them fails

let promise1 = Promise.resolve(1);
let promise2 = Promise.resolve(2);
let promise3 = Promise.reject(3);
const res = Promise.all([promise1,promise2,promise3]);
[[PromiseState]] is rejected, [[PromiseResult]] is 3
const res = Promise.all([promise1,promise2]);
This is very depressing. This is very depressing. [[Promise]] is very depressing
Copy the code

Promise.race

The parameters received are an array of N Promise objects.

Tips: The result returned is the new Promise, and the result state of the first completed Promise is the state of the final result.

let promise1 = Promise.resolve(1);
let promise2 = Promise.resolve(2);
let promise3 = Promise.reject(3);
const res = Promise.race([promise1,promise2,promise3]);
This is very depressing. [[Promise]] is very depressing. [[Promise]] is very depressing
Copy the code

Q&A

How do I change the state of a Promise object

  • resolve() // pending => fulfilled
  • reject() // pending => rejected
  • Throw ‘err’ // pending => rejected

Promise.then () returns the result

  • If an exception is thrown, the Promise object rejected is returned
  • If any value of the non-PROMISE type is returned, the Promise object in the Resolved state is returned
  • If a new promise is returned, the result of that promise becomes the new promise result

Why can a Promise be chained

Because then,catch,all,race, and so on all promise apis return the new Promise object. So you can continue to string together tasks by calling the promise’s methods

Promise’s abnormal penetration

  • When chained calls are made using a PROMISE’s THEN, the failed callback can be specified at the end
  • Any previous errors will be handled in the failed callback
let p1 = Promise.resolve(1);
p1.then((value) = >{
    console.log(11);
}).then((value) = >{
    throw 'err';
}).then((value) = >{
    console.log(22);
}).catch(err= >{
    console.log(err);
})
// Output: 11 err
Copy the code

Interrupt the chain of promise

let p1 = Promise.resolve(1);
p1.then((value) = >{
    console.log(11);
}).then((value) = >{
    console.log(22);
}).then((value) = >{
    console.log(33);
}).catch(err= >{
    console.log(err);
})
// Output: 11 22 33
Copy the code

So how do we break the callback association

let p1 = Promise.resolve(1);
p1.then((value) = >{
    console.log(11);
}).then((value) = >{
    console.log(22);
    return new Promise(() = >{});
}).then((value) = >{
    console.log(33);
}).catch(err= >{
    console.log(err);
})
// Output: 11 22
Copy the code

The answer is to return a Promise object with the pending state

Write a Promise

The code is iterated step by step, so you can see that I have written the comments more clearly ~

Constructor implementation

The first step is to complete the Promise constructor. The constructor, we think simply, takes an executable function that takes two arguments and changes the state and result of the Promise object. Ok, let’s do it!

tips:

  1. Attention! The throw Err can also change the state and result of a Promise!
  2. The promise state can only be changed once
// The first step on the Great Wall
function Promise(executor){
    this.promiseState = 'pending';
    this.promiseResult = null;
    const resolve = val= > {
        // The status can be changed only once
        if(this.promiseState ! = ='pending') return;
        This is very depressing. This is very depressing. This is very depressing
        this.promiseState = 'fulfilled';
        // 2. To change the state of the Promise object ([[promiseResult]]])
        this.promiseResult = val;
    }

    const reject = err= > {
        // The status can be changed only once
        if(this.promiseState ! = ='pending') return;
        ([[promiseState]]]); ([[promiseState]])
        this.promiseState = 'rejected';
        // 2. To change the state of the Promise object ([[promiseResult]]])
        this.promiseResult = err;
    }
    // Throw err, reject, reject, reject, reject, reject
    try{
        /* * Synchronously execute the executable function * The executable function takes two arguments, reject and resolve */
        executor(resolve,reject);
    } catch(err) { reject(err); }}Copy the code

The implementation of the then method

First, as mentioned earlier, then supports two parameters, the success and failure callbacks, which can be passed or not. Second, because of the asynchronous task problem and the support for multiple callbacks, we need to store the callbacks in an array, so we introduced a new variable, callbackList, and we need to note that the return result of the then is also a Promise object.

// The Great Wall still exists today
function Promise(executor){
    // Save the promise state
    this.promiseState = 'pending';
    // Save the promise result
    this.promiseResult = null;
    // Used to store a list of asynchronous callback functions
    this.callbackList = [];
    const resolve = val= > {
        // The status can be changed only once
        if(this.promiseState ! = ='pending') return;
        This is very depressing. This is very depressing. This is very depressing
        this.promiseState = 'fulfilled';
        // 2. To change the state of the Promise object ([[promiseResult]]])
        this.promiseResult = val;
        // Call the successful callback
        for(let callback of this.callbackList){ callback.onResolved(val); }}const reject = err= > {
        // The status can be changed only once
        if(this.promiseState ! = ='pending') return;
        ([[promiseState]]]); ([[promiseState]])
        this.promiseState = 'rejected';
        // 2. To change the state of the Promise object ([[promiseResult]]])
        this.promiseResult = err;
        // Call the failed callback
        for(let callback of this.callbackList){ callback.onRejected(err); }}// Throw err, reject, reject, reject, reject, reject
    try{
        /* * Synchronously execute the executable function * The executable function takes two arguments, reject and resolve */
        executor(resolve,reject);
    } catch(err) { reject(err); }}/ / then method
Promise.prototype.then = function(onResolved,onRejected){
    const self = this;

    // The then method returns a Promise
    return new Promise((resolve,reject) = > {
        // Encapsulate the processing of the return value
        const handleCallback = (callback) = > {
            // Reject if an error is thrown in the callback function
            try{
                // We need to determine the return value of the then method based on the return result of the callback
                // Now this will point to a promise object that returns, so use self
                const res = callback(self.promiseResult);
                if(res instanceof Promise) {// If the callback returns a Promise
                    res.then(val= > {
                        resolve(val);
                    },err= >{ reject(err); })}else{
                    // The result is not a Promiseresolve(res); }}catch(err){ reject(err); }}// Call the callback function
        if(this.promiseState === 'fulfilled'){
            handleCallback(onResolved);
        }
        if(this.promiseState === 'rejected'){
            handleCallback(onRejected);
        }
        /* * If it is pending, the asynchronous task calls the callback function when it changes state * so save the callback function * since the promise instance is large enough to specify multiple callbacks, so use an array */
        if(this.promiseState === 'pending') {this.callbackList.push({
                onResolved:() = > {
                    handleCallback(onResolved); 
                },
                onRejected:() = >{ handleCallback(onRejected); }})}})Copy the code

The implementation of the catch method

We use then methods to implement catch methods, but catch can handle exception penetration.

// I want to find someone to accompany me to the Great Wall
function Promise(executor){
    // Save the promise state
    this.promiseState = 'pending';
    // Save the promise result
    this.promiseResult = null;
    // Used to store a list of asynchronous callback functions
    this.callbackList = [];
    const resolve = val= > {
        // The status can be changed only once
        if(this.promiseState ! = ='pending') return;
        This is very depressing. This is very depressing. This is very depressing
        this.promiseState = 'fulfilled';
        // 2. To change the state of the Promise object ([[promiseResult]]])
        this.promiseResult = val;
        // Call the successful callback
        for(let callback of this.callbackList){ callback.onResolved(val); }}const reject = err= > {
        // The status can be changed only once
        if(this.promiseState ! = ='pending') return;
        ([[promiseState]]]); ([[promiseState]])
        this.promiseState = 'rejected';
        // 2. To change the state of the Promise object ([[promiseResult]]])
        this.promiseResult = err;
        // Call the failed callback
        for(let callback of this.callbackList){ callback.onRejected(err); }}// Throw err, reject, reject, reject, reject, reject
    try{
        /* * Synchronously execute the executable function * The executable function takes two arguments, reject and resolve */
        executor(resolve,reject);
    } catch(err) { reject(err); }}/ / then method
Promise.prototype.then = function(onResolved,onRejected){
    const self = this;
    // Handle exception traversal and set the default values for onResolved, onRejected. Because you don't have to pass either argument
    if(typeofonRejected ! = ='function'){
        onRejected = err= > {
            throwerr; }}if(typeofonResolved ! = ='function'){
        onResolved = val= > val;
    }
    // The then method returns a Promise
    return new Promise((resolve,reject) = > {
        // Encapsulate the processing of the return value
        const handleCallback = (callback) = > {
            // Reject if an error is thrown in the callback function
            try{
                // We need to determine the return value of the then method based on the return result of the callback
                // Now this will point to a promise object that returns, so use self
                const res = callback(self.promiseResult);
                if(res instanceof Promise) {// If the callback returns a Promise
                    res.then(val= > {
                        resolve(val);
                    },err= >{ reject(err); })}else{
                    // The result is not a Promiseresolve(res); }}catch(err){ reject(err); }}// Call the callback function
        if(this.promiseState === 'fulfilled'){
            handleCallback(onResolved);
        }
        if(this.promiseState === 'rejected'){
            handleCallback(onRejected);
        }
        /* * If it is pending, the asynchronous task calls the callback function when it changes state * so save the callback function * since the promise instance is large enough to specify multiple callbacks, so use an array */
        if(this.promiseState === 'pending') {this.callbackList.push({
                onResolved:() = > {
                    handleCallback(onResolved); 
                },
                onRejected:() = >{ handleCallback(onRejected); }})}})/ / catch method
Promise.prototype.catch = function(onRejected) {
    // We can use the then method directly
    return this.then(undefined,onRejected);
}
Copy the code

Promise.resolve method implementation

That’s easy. No more talking

/ / resolve method
Promise.resolve = function(val) {
    // The case for return values is described earlier and can be found in the Use of Promise chapter
    return new Promise((resolve,reject) = >{
        if(val instanceof Promise){
            val.then(val= > {
                resolve(val);
            }, err= > {
                reject(err);
            });
        }else{ resolve(value); }})}Copy the code

Reject method implementation

It’s much simpler. I don’t have to say anything

 / / reject method
Promise.reject = function(err) {
    // The case for return values is described earlier and can be found in the Use of Promise chapter
    return new Promise((resolve,reject) = >{ reject(err); })}Copy the code

All method implementation

It is easy to see how promise. all is used and how it is returned

// Let's review the use of the all method
//all
Promise.all = function(promiseList) {
    let count = 0;
    let res = [];
    const length = promiseList.length;
    return new Promise((resolve,reject) = >{
        for(let i = 0; i < length; i++){ promiseList[i].then(val= > {
                count++;
                res[i] = val;
                if(count === length){ resolve(res); }},err= >{ reject(err); }); }})}Copy the code

Promise.race method implementation

It’s easy to see how promise. race is used and how it returns

//race
// The end!
Promise.race = function(promiseList) {
    const length = promiseList.length;
    // The one who finishes first decides the result!
    return new Promise((resolve,reject) = >{
        for(let i = 0; i < length; i++){ promiseList[i].then(val= > {
                resolve(val);
            },err= >{ reject(err); }); }})}Copy the code

Complete code and detail handling

Tips: The detail callback function is asynchronous and we wrap it with setTimeout

function Promise(executor){
    // Save the promise state
    this.promiseState = 'pending';
    // Save the promise result
    this.promiseResult = null;
    // Used to store a list of asynchronous callback functions
    this.callbackList = [];
    const resolve = val= > {
        // The status can be changed only once
        if(this.promiseState ! = ='pending') return;
        This is very depressing. This is very depressing. This is very depressing
        this.promiseState = 'fulfilled';
        // 2. To change the state of the Promise object ([[promiseResult]]])
        this.promiseResult = val;
        setTimeout(() = > {
            // Call the successful callback
            for(let callback of this.callbackList){ callback.onResolved(val); }})}const reject = err= > {
        // The status can be changed only once
        if(this.promiseState ! = ='pending') return;
        ([[promiseState]]]); ([[promiseState]])
        this.promiseState = 'rejected';
        // 2. To change the state of the Promise object ([[promiseResult]]])
        this.promiseResult = err;
        setTimeout(() = > {
            // Call the failed callback
            for(let callback of this.callbackList){ callback.onRejected(err); }})}// Throw err, reject, reject, reject, reject, reject
    try{
        /* * Synchronously execute the executable function * The executable function takes two arguments, reject and resolve */
        executor(resolve,reject);
    } catch(err) { reject(err); }}/ / then method
Promise.prototype.then = function(onResolved,onRejected){
    const self = this;
    // Handle exception traversal and set the default values for onResolved, onRejected. Because you don't have to pass either argument
    if(typeofonRejected ! = ='function'){
        onRejected = err= > {
            throwerr; }}if(typeofonResolved ! = ='function'){
        onResolved = val= > val;
    }
    // The then method returns a Promise
    return new Promise((resolve,reject) = > {
        // Encapsulate the processing of the return value
        const handleCallback = (callback) = > {
            // Reject if an error is thrown in the callback function
            try{
                // We need to determine the return value of the then method based on the return result of the callback
                // Now this will point to a promise object that returns, so use self
                const res = callback(self.promiseResult);
                if(res instanceof Promise) {// If the callback returns a Promise
                    res.then(val= > {
                        resolve(val);
                    },err= >{ reject(err); })}else{
                    // The result is not a Promiseresolve(res); }}catch(err){ reject(err); }}// Call the callback function
        if(this.promiseState === 'fulfilled') {setTimeout(() = >{ handleCallback(onResolved); })}if(this.promiseState === 'rejected') {setTimeout(() = >{ handleCallback(onRejected); })}/* * If it is pending, the asynchronous task calls the callback function when it changes state * so save the callback function * since the promise instance is large enough to specify multiple callbacks, so use an array */
        if(this.promiseState === 'pending') {this.callbackList.push({
                onResolved:() = > {
                    handleCallback(onResolved); 
                },
                onRejected:() = >{ handleCallback(onRejected); }})}})/ / catch method
Promise.prototype.catch = function(onRejected) {
    // We can use the then method directly
    return this.then(undefined,onRejected);
}
/ / resolve method
Promise.resolve = function(val) {
    // The case for return values is described earlier and can be found in the Use of Promise chapter
    return new Promise((resolve,reject) = >{
        if(val instanceof Promise){
            val.then(val= > {
                resolve(val);
            }, err= > {
                reject(err);
            });
        }else{ resolve(value); }})}/ / reject method
Promise.reject = function(err) {
    // The case for return values is described earlier and can be found in the Use of Promise chapter
    return new Promise((resolve,reject) = >{ reject(err); })}//all
Promise.all = function(promiseList) {
    let count = 0;
    let res = [];
    const length = promiseList.length;
    return new Promise((resolve,reject) = >{
        for(let i = 0; i < length; i++){ promiseList[i].then(val= > {
                count++;
                res[i] = val;
                if(count === length){ resolve(res); }},err= >{ reject(err); }); }})}//race
Promise.race = function(promiseList) {
    const length = promiseList.length;
    // The one who finishes first decides the result!
    return new Promise((resolve,reject) = >{
        for(let i = 0; i < length; i++){ promiseList[i].then(val= > {
                resolve(val);
            },err= >{ reject(err); }); }})}Copy the code

That’s the end of our handwritten Promise chapter

conclusion

Tips: I really want to make fun of myself. Why do I always end my blog with either chicken soup or chicken soup

PromiseThe word promises. I take this article as my new beginning, starting from the foundation of precipitation, refused to aim too high.

I promise that my beliefs and pursuits will be consistent

I promise to show myself the beauty of the world

I promise that I will always strive to be a good engineer

I promiseNo matter what the result is, you will try your best to courageously pursue whatever you like without leaving any regrets

.

Finally, may my old blood always be warm, and the world always be at peace

Hey, guys