preface

I'm sure you're familiar with Promise and will use it a lot in project development. But how exactly does it work internally?Copy the code

1. Basic use of Promises

One of the characteristics of javaScript is that it is single-threaded. Time-consuming operations (such as network requests) are placed in asynchronous queues to execute asynchronously so as not to block the work of the main process. But this can be a big problem, as some Ajax requests rely on the return value of the previous Ajax request, leading to the infamous hell callback problem. Promise was born to address this phenomenonCopy the code

Promise is a constructor that must accept a function as an argument, which we call executor. Executor also accepts two arguments, resolve and reject, which are also functions.

 let p1 = new Promise(function(resolve,reject){
     // Write the code for asynchronous operations here
     setTimeout(function(){
         resolve(1)})})Copy the code

This is a big pity. The then function accepts two types of parameters, the first is onFulfilled. (This is a big pity. OnRejected = onRejected = onRejected = onRejected = onRejected = onRejected = onRejected = onRejected

    p1.then(function(data){
        console.log(data)
    },function(err){
        console.log(err)
    })
Copy the code

2. The core concept of Promise

Promise objects have three internal states

  • Pending

  • This is a big pity.

  • Rejected (failed)

The state of a promise can only be changed by the result of asynchronous processing; it cannot be changed by outsiders. The initial state is pending. After the asynchronous operation succeeds, the resolve function is executed, and the state of the promise changes from pending to depressing. If the asynchronous operation fails, the state of the promise changes from pending to Rejected. Once the state of a promise changes, it cannot be changed.

3. Promise is easy to implement

A promise is a constructor that takes a function as an argument, has a state identifier inside it, and has two methods that can change the state: resolve: There is also a then method, which receives callback functions after success and failureCopy the code
// Let's try to write a promise that does this
// Define the three states of Promise
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class Promise{
    constructor(executor){
        if(typeofexecutor ! = ='function') {throw new TypeError('executor must be a function! ')}this.status = PENDING; // Initial state
        this.value = undefined;
        this.reason = undefined;
        
        const resolve = function(res){
            if(this.status === PENDING){
                this.status = FULFILLED;
                this.value = res; }}const reject = function(err){
            if(this.status === PENDING){
             this.status = REJECTED;
             this.reason = err
            }
           }
           // Try catch is used to catch errors reported during the execution of a function
           try{
               executor(resolve,reject)
           }catch(err){
           Reject if there is an error
               reject(err)
           }
    }
    then(onFulfilled,onReject){
        if(this.status === FULFILLED){
            onFulfilled(this.value)
        }
         if(this.status === REJECTED){
            onFulfilled(this.message)
        }
    }
}
Copy the code
The code above gives you a quick look at the execution structure and state changes inside promise, but it doesn't support asynchronous and chained calls. Let's move on to the next levelCopy the code

4. How does Promise implement asynchronous processing and chain invocation

  • asynchronous

Async: async: promise (); async: promise (); async: promise (); async: promise (); async: promise (); async: promise () And since the then method can be called by the same promise multiple times, we can create an array of callbacks to store the callbacks that need to be executed when the state changes, and then execute those callbacks when the state changes. At this point, the Promise can handle both synchronous and asynchronous operations

Asynchronous processing is implemented using the publish-subscribe model. In the then function, when the promise is pending, the functions that need to be executed in the (success/failure) state are collected and placed into the success/failure callback array. When the internal state of the promise changes, Functions collected from an array of executed (success/failure) callbacks based on the current stateNote: If you execute a "THEN" multiple times for the same promise, then callbacks will either follow successful logic or fail logic!
    class Promise{
    constructor(executor){
        if(typeofexecutor ! = ='function') {throw new TypeError('executor must be a function! ')}this.status = PENDING; // Initial state
        this.value = undefined;
        this.reason = undefined;
        
        // OnonledCallbacks is used to collect callback functions to execute during the depressing state
        this.onFulfilledCallbacks = [];
        
        //onRejectedCallbacks is used to collect the REJECTED state callback function to execute
        this.onRejectedCallbacks = [];
        
        const resolve = function(res){
            if(this.status === PENDING){
                this.status = FULFILLED;
                this.value = res;
            }
            let fn;
            // Execute the function in the queue once and empty the queue publication
            while(fn = this.onFulfilledCallbacks.shift()){ fn(); }}const reject = function(err){
            if(this.status === PENDING){
                 this.status = REJECTED;
                 this.reason = err
            }
             let fn;
            while(fn = this.onRejectedCallbacks.shift()){ fn(); }}}try{
               executor(resolve,reject)
           }catch(err){
               reject(err)
           }
    }
    then(onFulfilled,onReject){
        if(this.status === PENDING){
            / / subscribe
            this.onFulfilledCallbacks.push(() = >{
                onFulfilled(this.value)
            });
            this.onRejectedCallbacks.push(() = > {
                onReject(this.reason)
            });
        }
        if(this.status === FULFILLED){
            onFulfilled(this.value)
        }
         if(this.status === REJECTED){
            onFulfilled(this.reason)
        }
    }
}
Copy the code
  • Chain calls

Promise1. Then (onelecd1, onRejected1). Then (onelecled2, onRejected2). Therefore, the then method must return a new Promise object, and the chain invocation of the Promise must ensure that the next Promise will be implemented after the current Promise state is fulfilled

       then(onFulfilled,onReject){
          // * If the callback passed is not the flower of the function, there is a default callback that returns the promise value
          onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) = > value;
          onReject = typeof onReject === 'function' ? onReject : (err) = > err;
        // New Promise will execute executor immediately, so you can put logic from the then method into executor functions
        let promise2 =  new Promise((resolve,reject) = > {
            if( this.status === PENDING){
                / / subscribe
                this.onFulfilledCallbacks.push(() = >{
                    try {
                        resolvePromise( this.value,promise2, onFulfilled,resolve,reject)
                    } catch (error) {
                        reject(error)
                    }
                   
                });
                this.onRejectedCallbacks.push(() = > {
                    try {
                     resolvePromise( this.reason,promise2,onReject,resolve,reject)
                    } catch (error) {
                        reject(error)
                    }
                });
            }
            if( this.status === FULFILLED){
                setTimeout(() = > {
                try {
                    resolvePromise(this.value,promise2, onFulfilled,resolve,reject)
                } catch (error) {
                    reject(error)
                }
                },0)}if(this.status === REJECTED){
                setTimeout(() = > {
                try {
                    resolvePromise( this.reason,promise2,onReject,resolve,reject)
                } catch (error) {
                    reject(error)
                }
                },0)}})return promise2;
       
    }
    
    // The helper method resolePromise
    function resolvePromise(value,promise2,callback,resolve,reject){
    // Execute the callback and get the return of the callback
    let x =  callback(value);
    if(x === promise2){
        return reject(new TypeError('TypeError'))}// This is used to resolve that once the state of the promise changes, no more state changes will be performed
    let called = false;
    if(typeof x === 'function'| | -typeof x === 'object'&& x ! = =null)) {try {
            let then = x.then
            // If res has the then method, you can almost guarantee that the return value will be a Promise object
            if(typeof then === 'function') {if(called) return true
                then.call(x, (y) = > {
                    called = true
                    // resolve(y)
                    // Recursive call to resolve that the argument passed to resolve() is still a promise
                    resolvePromise(y,promise2,(value) = > value,resolve,reject)
                }, (r) = > {
                    called = true
                    reject(r)
                });
            }else {
                resolve(x)
            }
        } catch (error) {
            reject(error)
        }
     
    }else{ resolve(x); }}Copy the code

Until now, the internal implementation principle of Promise is basically very clear. The implementation principle of chain call of then function is somewhat complicated. The core is that then method needs to return a new Promise, and the state of the previous Promise must be changed before the next Promise can be implemented. It’s easier to understand the internal state of promises and their logic for changing them

Other apis for Promise

  • catch

Catch is a syntactic sugar notation that executes then(undefined,(err)=>{}). Since the code logic in the Promise source code is in a try catch, the program will execute the error-listening callback function if it fails.

catch(onError){
    this.then(undefined,onError)
}
Copy the code
  • finally

Finally means that the method is executed regardless of the final state of the promise. The finally callback takes no arguments, indicating that the finally function is executed independent of the state

// A static resolve method is required that returns a Promise
static resolve(val){
       const p = new Promise(function(){});
       p.status = 'fulfilled';
       p.value = val;
       return p;
   }
finally(cb){
    return this.then(function(res){
        res => Promise.resolve(cd()).then(() = > return res)
    },function(err){
        res => Promise.resolve(cd()).then(() = > return err)
    })
}
Copy the code
  • Static method all

Promise’s All method, which encapsulates multiple Promise instances into a single Promise object, This is a big pity. The state of the current promise object will become fulfilled, which is a big pity. When one of the states of the passed Promise instance changes to Rejected, The status of the promised object changes to Rejected,q and the value of the promised object is the value of the Promise instance whose status changes to Rejected

    Promise.all = function(arr){
    return new MyPromise(function(resolve,reject){
        var args = [];
        var len = arr.length - 1;
        function handler(val,i){
            if(typeof val === 'function' || typeof val === 'object') {if(val instanceof MyPromise){
                    val.then(res= > {
                        args[i] = res
                    },err= > {
                        reject(err)
                    })
                }
            }
            args[i] = val;
            if(i === len){
                resolve(args)
            }
        }
        arr.forEach((item,i) = > {
            handler(item,i)
        })
    })

}
Copy the code
  • Static method RACE

The race method is simply understood as a race. Multiple promise instances are passed in as a parameter, and the state of the returned Promise object will change if the state of the promise instance changes first

    Promise.race = function(arr){
    return new MyPromise(function(resolve,reject){
        arr.forEach(item= > {
            Promise.resolve(item).then(res= > {
                resolve(res);
            },function(err){ reject(err); })})})Copy the code

conclusion

It took a long time to summarize the output, and in the process I also re-understood a lot. The article has the inadequacy place, still hope everybody points out give advice! End scatter flowers 🎉🎉🎉Copy the code