Promise is a solution to asynchronous programming that makes more sense and is more powerful than traditional solutions — callback functions and events. It was first proposed and implemented by the community, and ES6 has written it into the language standard, unifying usage, and providing Promise objects natively. @ ruan teacher

Let’s look atpromiseSeveral uses of:

  • Promise.prototype.then: The then method takes two arguments. The first argument is a callback that will be executed when the Promise succeeds, the second callback that will be executed when the Promise fails, and the then method returns a new Promise instance
    new Promise((resolve, reject) => resolve()).then(() => {
        console.log('success');
    }, () => {
        console.log('err')})Copy the code
  • Promise.prototype.catch: Used to specify the callback function when an error occurs
If reject is not specified, the catch function is finally called: new Promise((resolve, reject) => {reject('err');
})
.then()
.catch(e => {
    console.log(e); // => Error: err
})
Copy the code
  • Promise.resolve: a method defined in the Promise class itself that can be called promise.resolve (), which directly changes the state to a success state
Promise.resolve('hahaha').then(data => { console.log(data); / / = >'hahaha'
})
Copy the code
  • Promise.reject: A method defined in the Promise class itself that can be called through promise.reject (), effectively changing the state directly to fail
Promise.reject('hahaha').then(data => { console.log(data); / / = >'hahaha'
})
Copy the code
  • Promise.prototype.all: Returns the results of multiple Promise implementations in an array
let promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
          resolve('promise1');
    }, 1500);
})

let promise2 = new Promise((resolve, reject) => {
    setTimeout(() => {
          resolve('promise2'); }, 2000); }) Promise.all([promise1, promise2]).then(data => { console.log(data); / / = > ["promise1"."promise2"]})Copy the code
  • .

Let’s implement a fit ourselvesPromiseA + specificationThe Promise of

Let’s start with a simple promise:

console.log(1)
new Promise(() => {
    console.log(2)
});
console.log(3)
Copy the code

Since we all know that promises are asynchronous, they should print 1,3,2 and then we run them and find that they print 1,2, and 3 when we use then:

console.log(1)
Promise.resolve().then(() => {
    console.log(2);
})
console.log(3)

Copy the code

The result is the same as we expected, with 1,3, and 2 being printed because the Promise callback executes immediately and only the THEN method is asynchronous

A promise must be in one of three states: pending, fulfilled, or rejected.

  1. This is a big pity. Promise must have three states pending(fulfilled), which must be fulfilled(rejected).
    • When the state ispendingwhen
      • This state can be changed to another state (depressing or Rejected).
    • When the state isfulfilledwhen
      • Cannot transition to another state
      • There has to be onevalue(Value after success)
    • When the state isrejectedwhen
      • Cannot transition to another state
      • There has to be onereason(Cause of failure)
Class Promise {constructor(executor) {// Each Promise instance has its own three states. We use a variable to hold the current state this.status ='pending'; // This. Value; // This. Reason; // The method used to change the current state to the successful stateletResolve = val => {// State and value can be changed only if the current state is' pending 'if (this.status === 'pending') {
                this.value = val;
                this.status = 'fulfilled'; }} // The method used to change the current state to a failed stateletReject = Reason => {// State and value can be changed only if the current state is' pending 'if (this.status === 'pending') {
                this.reason = reason;
                this.status = 'rejected'; } // Execute executor(resolve, reject) {// execute executor(resolve, reject) {// execute executor(resolve, reject); } catch (e) {reject(e) {reject(e) {reject(e); }}}Copy the code
  1. thenmethods

A promise must provide a then method to get the current or final value or reason

The then method of Promise has two parameters, which are onFulfilled when it succeeds and onFulfilled when it fails

  • onFulfilledonRejectedBoth are optional parameters
    • If not, we need to set a default method
  • ifonFulfilledIs afunction
    • It must be presentpromiseThe state of becomingfulfilledAnd then it gets called, and puts the promise’svalue(the value after success) as its first argument
    • The state isfulfilledYou couldn’t call it before
    • Can only be called once
  • ifonRejectedIs afunction
    • It must be presentpromiseThe state of becomingrejectedAnd then it gets called, and puts the promise’sreason(Cause of failure) as his first parameter
    • The state isrejectedYou couldn’t call it before
    • Can only be called once
promise.then(onFulfilled, onRejected)
Copy the code
class Promise { constructor(executor) {... }then(onFulfilled, onRejected) {// If the current state is successful, we will perform the successful callback and pass the saved successful valueif (this.status === 'fulfilled') { onFulfilled(this.value); } // If the current state is failed, we execute the failed callback and pass the saved failure reasonif (this.status === 'rejected') { onRejected(this.reason); }}}Copy the code

Okay, so now we’re ready to test it out

new Promise((resolve, reject) => {
    resolve('perfect'); }).then(data => { console.log(data); // => perfect}Copy the code

Perfect, but then the question is, what if our resolve or reject is asynchronous?

new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('perfect');
    }, 1000);
}).then(data => {
    console.log(data); 
}, err => {
    console.log(err);
})
Copy the code

OnFulfilled and onRejected will not print something, that means they are not executed. Let me think…

This is because if our resolve or reject is asynchronous, when our THEN executes, the state is still pending, so of course nothing executes. We can save onFulfilled and onRejected first, and perform the corresponding callback successively when the state changes. A: Is that it? B: Yes, that’s the subscription publishing model. Let’s rewrite this:

class Promise {
    constructor(executor) {
        this.status = 'pending'; this.value; this.reason; // This. OnSuccessCallback = []; // This. OnErrorCallback = [];let resolve = val => {
            if (this.status === 'pending') {
                this.value = val;
                this.status = 'fulfilled'; / / state changes Executed in sequence to the success of this callback. OnSuccessCallback. ForEach (fn = > fn ()); }}let reject = reason => {
            if (this.status === 'pending') {
                this.reason = reason;
                this.status = 'rejected'; / / state changes Failed to perform, in turn, the callback this. OnErrorCallback. ForEach (fn = > fn ()); } // Execute executor(resolve, reject) {// execute executor(resolve, reject) {// execute executor(resolve, reject); } catch (e) {reject(e) {reject(e) {reject(e); }}then(onFulfilled, onRejected) {
        
        if (this.status === 'fulfilled') {
            onFulfilled(this.value);
        }
        
        if (this.status === 'rejected') {
            onRejected(this.reason);
        }
        
        if (this.status === 'pending') {/ / to save success callback callbacks and failure were to wait for state changes, and in turn perform corresponding method enclosing onSuccessCallback. Push (() = > {onFulfilled (enclosing the value). }); this.onErrorCallback.push(() => { onRejected(this.reason); }); }}}Copy the code
  • OnFulfilled and onRejected are both asynchronous calls.

  • OnFulfilled and onRejected must be performed as a function

  • The then method must return a promise

    promise2 = promise1.then(onFulfilled, onRejected)
    Copy the code
    • ifonFulFilled or onRejectedA value is returnedx, run the Promise resolver
    • ifonFulfilled or onRejectedAn exception was throwne.promise2State must berejectedAnd willeAs aonRejectedThe parameters of the
    • ifonFulfilledIs not afunctionandpromise1isfulfilled.promise2Must also befulfilledAnd use andpromise1The samevalue
    • ifonRejectedIs not afunctionandpromise1isrejected.promise2Must also berejectedAnd use andpromise1The samereason

    We often use promises.then ().then().then().then()… This is called a chained call, so how do you proceed with the then method, which the specification says must return an instance of a promise, in order to implement the chained call

    class Promise {
        contructor() {... }then(onFulfilled, onFulfilled) {// If onFulfilled and onFulfilled are not a function, we will give a default value onFulfilled = typeof onFulfilled ==='function' ? onFulfilled : val => val;
            onRejected = typeof onRejected === 'function'? onRejected : err => { throw err }; // We package everything into a Promise instance and return that instance so we can implement the chained callletpromise2; Promise2 = new Promise((resolve, reject) => {// this is a big pity.if (this.status === 'fulfilled') {
                    setTimeout(() => {try {// with a return value x, run resolvePromiseletx = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); }}, 0); }if (this.status === 'rejected') {
                    setTimeout(() => {try {// with a return value x, run resolvePromiseletx = onRejected(this.reason);; resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); }}, 0); }if (this.status === 'pending') {/ / to save success callback callbacks and failure were to wait for state changes, and in turn perform corresponding method enclosing onSuccessCallback. Push (() = > {setTimeout(() => {try {// with a return value x, run resolvePromiseletx = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); }}, 0); }); this.onErrorCallback.push(() => {setTimeout(() => {try {// with a return value x, run resolvePromiseletx = onRejected(this.reason);; resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); }}, 0); }); }});returnpromise2; }}Copy the code

    If there is a return value x, run the promise resolver resolvePromise.

    Let’s start with the specification:

    The promise resolver is a function that takes promise2 and x as parameters

  • If promise2 and x are a promise, raise a TypeError

  • If x is an object or function

    • Use the variablethenstoragex.then
    • ifx.thenWill throw an exceptione, calling promise’srejectAnd willeAs its argument
    • ifthenIs afunction, the use ofcallIt’sthisPoint to thexAnd its first argument isresolvePromiseThe second argument isrejectPromise:
      • whenresolvePromisebeyThe parser continues to execute when the value is called
      • whenrejectPromiseWhen executed, the promise’s is calledrejectAnd will fail reasonsrAs its argument
    • ifthenIs not aobject or function, calling promise’sresolveAnd will bexAs its argument
  • If x is not an object or function, call the Promise’s resolve and take x as its argument

There are only two points:

  1. If promise2 is equal to x, it raises a TypeError, so let’s look at that first

    letP = new Promise((resolve, reject) => {// Return the current Promise instancereturn p;
    });
    
    p.then(data => {
        console.log(data);
    }, err => {
        console.log(err);
    });
    Copy the code

    Running the above code, we’ll see that the Promise throws an exception that tells us TypeError: Chaining cycle detected for promise, this is because the success or failure of P depends on itself and it waits for its own execution results, so it will neither succeed nor fail

    1. ifonFulFilled or onRejectedA value is returnedx, run the Promise resolverresolvePromise

    The return value x could be a constant, it could be an object, it could be a promise, and what this program does is if x is a promise, it resolves x all the way to the constant

    letp = new Promise((resolve, reject) => { resolve(new Promise((resolve, reject) => { resolve(1111); }})))Copy the code
    letResolvePromise = (promise2, x, resolve, reject) => {// Throw a type error if promise2 and x are equalif (promise2 === x) {
            return reject(new TypeError('wrong')); } // Only one resolvePromise call is allowedletcalled; // Continue parsing if x is not a constantif(x ! == null && (typeof x ==='function' || typeof x === 'object') {// We might get an error when we call x. Chen, because we might use a try {from someone else's Promise library.let then= x.then; / / ifthenIs a function, prove that x is a promise, and keep parsingif (typeof then= = ='function') {
                    then.call(x, y => {
                        if (called) {
                            return;
                        } else {
                            called = true;
                        }
                        resolvePromise(promise2, y, resolve, reject);
                    }, r => {
                        if (called) {
                            return;
                        } else {
                            called = true; } reject(r); })}else{// indicate that x may be a normal object, not a promise resolve(x); } } catch (e) {if (called) {
                    return;
                } else {
                    called = true; } reject(e); }}elseResolve resolve(x); }}Copy the code

    Resolve Promise. Reject Promise. All

    class Promise { contructor {... }then() {... Reject (value) {// Promise. Reject (value) {return new Promise((resolve) => {
                resolve(value);
            })
        }
        static reject(reason) {
            returnnew Promise((resolve, reject) => { reject(reason); })} // Static all(promises) {returnNew Promise(resolve, reject) => {// Save the resultlet arr = [];
                let index = 0;
                
                const saveData = (i, data) => {
                    arr[i] = data;
                    if(++index === promises.length) { resolve(arr); }}for (let i = 0; i < promises.length; i++) {
                
                    promises[i].then(data => {
                        saveData(i, data);
                    }, reject)
                }
            })
        }
    }
    Copy the code

    Okay, let’s do a full version of promise

    class Promise {
        contructor() {
            this.status = 'pending'; this.value; this.reason; // This. OnSuccessCallback = []; // This. OnErrorCallback = [];let resolve = val => {
                if (this.status === 'pending') {
                    this.value = val;
                    this.status = 'fulfilled'; / / state changes Executed in sequence to the success of this callback. OnSuccessCallback. ForEach (fn = > fn ()); }}let reject = reason => {
                if (this.status === 'pending') {
                    this.reason = reason;
                    this.status = 'rejected'; / / state changes Failed to perform, in turn, the callback this. OnErrorCallback. ForEach (fn = > fn ()); } // Execute executor(resolve, reject) {// execute executor(resolve, reject) {// execute executor(resolve, reject); } catch (e) {reject(e) {reject(e) {reject(e); } } static resolve(value) {return new Promise((resolve) => {
                resolve(value);
            })
        }
        static reject(reason) {
            return new Promise((resolve, reject) => {
                reject(reason);
            })
        }
        
        static all(promises) {
            returnNew Promise(resolve, reject) => {// Save the resultlet arr = [];
                let index = 0;
                
                const saveData = (i, data) => {
                    arr[i] = data;
                    if(++index === promises.length) { resolve(arr); }}for (let i = 0; i < promises.length; i++) {
                
                    promises[i].then(data => {
                        saveData(i, data);
                    }, reject)
                }
            })
        }
        
        then(onFulfilled, onFulfilled) {// If onFulfilled and onFulfilled are not a function, we will give a default value onFulfilled = typeof onFulfilled ==='function' ? onFulfilled : val => val;
            onRejected = typeof onRejected === 'function'? onRejected : err => { throw err }; // We package everything into a Promise instance and return that instance so we can implement the chained callletpromise2; Promise2 = new Promise((resolve, reject) => {// this is a big pity.if (this.status === 'fulfilled') {
                    setTimeout(() => {try {// with a return value x, run resolvePromiseletx = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); }}, 0); }if (this.status === 'rejected') {
                    setTimeout(() => {try {// with a return value x, run resolvePromiseletx = onRejected(this.reason);; resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); }}, 0); }if (this.status === 'pending') {/ / to save success callback callbacks and failure were to wait for state changes, and in turn perform corresponding method enclosing onSuccessCallback. Push (() = > {setTimeout(() => {try {// with a return value x, run resolvePromiseletx = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); }}, 0); }); this.onErrorCallback.push(() => {setTimeout(() => {try {// with a return value x, run resolvePromiseletx = onRejected(this.reason);; resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); }}, 0); }); }});returnpromise2; }}letResolvePromise = (promise2, x, resolve, reject) => {// Throw a type error if promise2 and x are equalif (promise2 === x) {
            return reject(new TypeError('wrong')); } // Only one resolvePromise call is allowedletcalled; // Continue parsing if x is not a constantif(x ! == null && (typeof x ==='function' || typeof x === 'object') {// We might get an error when we call x. Chen, because we might use a try {from someone else's Promise library.let then= x.then; / / ifthenIs a function, prove that x is a promise, and keep parsingif (typeof then= = ='function') {
                    then.call(x, y => {
                        if (called) {
                            return;
                        } else {
                            called = true;
                        }
                        resolvePromise(promise2, y, resolve, reject);
                    }, r => {
                        if (called) {
                            return;
                        } else {
                            called = true; } reject(r); })}else{// indicate that x may be a normal object, not a promise resolve(x); } } catch (e) {if (called) {
                    return;
                } else {
                    called = true; } reject(e); }}elseResolve resolve(x); }}Copy the code