Promise,

Why do WE need Promise

JavaScript is a single-threaded language, so in the early days we dealt with asynchronous scenarios mostly through callback functions. For example, sending an Ajax request in a browser is a common asynchronous scenario, where the request is sent and the server responds some time before we get the result. If we want to perform an operation after the asynchrony ends, the only way to do so is through a callback function.

const dynamicFunc = function (cb) {
    setTimeout(() = > {
        cb()
    }, 1000)
}
dynamicFunc(function () {
    console.log(123)})Copy the code

In this example dynamicFunc is an asynchronous function. SetTimeout will put the cb function into the event queue after 1s, and call the CB function when the main thread is finished executing. Following the above call, the result 123 will be printed after a delay of 1s. Similarly, if there is further output at the end of the asynchronous function, multiple asynchronous functions need to be nested, which is not good for subsequent maintenance.

How is Promise used

In an advanced browser environment that supports ES6, we create a Promise instance with New Promise(). This constructor takes a function that takes two arguments, resolve and reject, to change the state of the current instance to completed or rejected.

function promise1() {
    return new Promise((resolve, reject) = > {
        setTimeout(() = > {
            console.log('Output after 1s')
            resolve();
        }, 1000)})}function promise2() {
    return new Promise((resolve, reject) = > {
        setTimeout(() = > {
            console.log('Output after 2s')
            resolve();
        }, 2000)})}Copy the code

Promise1 () => promise2(); promise1() => promise2(); promise1() => promise2();

After executing in the browser, you can see that after 1s comes 1s output, after 2s comes 2s output. In this example, we can see that the current promise will execute the next promise function in the then method if the state changes to complete (resolve method executed).

Similarly, if a promise becomes rejected (reject method executed), it goes into subsequent exception handlers.

function promise3() {
    return new Promise(function (resolve, reject) {
        var random = Math.random() * 10;
        setTimeout(function () {
            if (random >= 5) {
                resolve(random);
            } else{ reject(random); }},1000);
    });
}

var onResolve = function (val) {
    console.log('Done: The output number is:', val);
};
var onReject = function (val) {
    console.log('Rejected: the output number is:', val);
}

promise3().then(onResolve, onReject);

// A promise whose state becomes rejected can be intercepted using the catch method
promise3().catch(onReject).then(onResolve);

// You can also intercept a rejected promise with a try catch
try {
    promise3().then(onResolve);
} catch (e) {
    onReject(e);
}
Copy the code

This example uses three ways to intercept promises that eventually become rejected: using the second argument to then, using the.catch method to catch exceptions thrown by the promise ahead, and using a try catch to intercept exceptions thrown by the promise in a block of code.

We also found that when we call resolve and Reject when we change the promise state, we can also pass arguments to the next function executed in the THEN. In this example, we pass the randomly generated number to resolve and reject, so that we can get the value when we execute the function in THEN.

Conclusion:

  1. Promise has three states,ongoing Has been completed Has refused to, the state in progress can be changed to completed or rejected, and no further changes can be made after the state has been changed (for example, it cannot be changed from Completed to rejected)
  2. The Promise constructor in ES6 requires passing in a function, taking two function arguments, and changing the current Promise after the first argumentHas been completedState, which becomes when the second argument is executedHas refused tostate
  3. The. Then method allows you to continue executing the next function or promise when the previous promise becomes completed. At the same time, you pass an argument through resolve or reject to pass an initial value to the next function or promise
  4. Rejected promises can then be caught using either the. Catch method or the second argument to the. Then method or a try catch

Promise specification interpretation

Any object or function that meets the promise specification can become A promise. Promises /A+

Above, we learned how to use the whole promise, how to create a promise, how to use it, and how to modify the callback function into a promise. In this section, we will go through promises/A+ specifications in detail to understand the details in the process of using promises from the specification level.

The term

  • Promise: Promise is a possessionthenMethod that behaves in accordance with this specification
  • Having the then method (thenable) : is a definitionthenThe object or function of a method
  • Value: any valid JavaScript value (including undefined, thenable, and promise)
  • Exception: YesthrowA value thrown by the statement
  • Reason: indicates a reason for rejecting a promise

requirements

The state of the promise

The current state of a Promise must be one of three states: Pending, Fulfilled, and Rejected.

  • In the wait state, a promise must satisfy one condition: it can beHas been completedorHas refused to
  • When a promise is completed, it must satisfy the following conditions: 1. It cannot change into any other state 2. Must have an immutable value
  • When a promise is rejected, the following conditions must be met: 1. It cannot change into any other state 2. There must be an immutable cause

There must be a then method

A promise must provide a THEN method to access its current value and reason. The promise’s then method accepts two parameters: Promise. Then (onFulfilled, onRejected), both of which are optional parameters, and they are functions. If onFulfilled or onRejected is not a function, you need to ignore them.

  • ifonFulfilledIt’s a function
    • whenpromiseIt must be called after execution, and its first argument is the result of a promise
    • whenpromiseIt cannot be called until execution ends
    • It cannot be invoked more than once
  • ifonRejectedIt’s a function
    • whenpromiseThe reason why it must be invoked if the execution is rejected, and its first argument is a promise
    • whenpromiseIt cannot be called until execution is rejected
    • It cannot be invoked more than once
  • Should not be called until the execution context stack contains only the platform codeonFulfilledoronRejected
  • onFulfilledandonRejectedMust be called as a normal function (i.e., a non-instantiated call, such that the inside of the functionthisPoint to window in non-strict mode)
  • thenThe method can be the samepromiseCall several times
    • whenpromiseWhen successfully executed, allonFulfilledCallbacks must be made in the order in which they were registered
    • whenpromiseWhen rejected, allonRejectedCallbacks must be made in the order in which they were registered
  • thenMethod must return onepromiseobjectpromise2 = promise1.then(onFulfilled, onRejected)
    • As long asonFulfilledoronRejectedReturn a valuexPromise2 will enter the ondepressing state
    • As long asonFulfilledoronRejectedThrow an exceptione, promise2 must reject execution and return a reasone
  • ifonFulfilledIs not a function andpromise1The state becomes completed,promise2Must execute successfully and return the same value
  • ifonRejectedIs not a function andpromise1The status becomes Rejected,promise2You must perform a reject callback and return the same cause
var promise1 = new Promise((resolve, reject) = > { reject(); });
promise1
    .then(null.function () {
        return 123;
    })
    .then(null.null)
    .then(null.null)
    .then(
        () = > {
            console.log('Promise2 completed');
        },
        () = > {
            console.log('Promise2 has been rejected');
        });
Copy the code

Promise’s resolution process

** Promise resolution is an abstract operation that takes a promise and a value, which we express as [[Resolve]](promise, x).

promise.then(x= > {
    console.log('will execute this function, passing in the value of the x variable:', x)
})
Copy the code

If X has a then method and looks like a Promise, the resolver tries to make the Promise accept x’s state; Otherwise it implements the promise with the value of x.

  • ifpromiseandxTo point to the same objectTypeErrorRefuse to perform for reasonpromise
  • ifxforpromise
    • ifxIn the waiting state,promiseMust remain in wait state untilxTo be carried out or rejected
    • ifxIn the execution state, execute with the same valuepromise
    • ifxIn the rejection state, reject for the same reasonpromise
var promise1 = function () {
    return new Promise(function (resolve) {
        setTimeout(function () {
            console.log(1);
            resolve();
        }, 1000)}); }var promise2 = function () {
    return new Promise(function (resolve) {
        setTimeout(function () {
            console.log(2);
            resolve();
        }, 2000);
    });
}
promise1()
    .then(function () {
        return promise2(); // An instance of promise is returned
    })
    .then(function () { console.log('Done')},function () { console.log('Rejected')});Copy the code
  • ifxFor Object or function (uncommon)
    • Try executing x. teng first
    • An error is thrown if the value of x. Chen is takene, theeReject for a reasonpromise
    • ifthenTheta is a function of thetaxAs the scope of the functionthisThe call. Pass two callback functions as arguments, the first of which is calledresolvePromiseThe second one is calledrejectPromise:
      • ifresolvePromiseIn order to valueyRun ‘[[Resolve]](promise, y)
      • ifrejectPromiseTo according to therIs called with an argument, then with an argumentrRefused topromise
      • ifresolvePromiseandrejectPromiseAre called, or are called by the same parameter more than once, the first call is preferred and other calls are ignored
      • If the callthenMethod throws an exceptione
        • ifresolvePromiseorrejectPromiseIf yes, ignore it
        • Or otherwiseeRefusal on grounds of proofpromise
    • ifthenIs not a function of thetaxFor the parameter will bepromiseChanges to the completed state
  • ifxIs not an object or a functionxFor the parameter will bepromiseTo the completed state ** (important and common) **

Static methods on the Promise constructor

Promise.resolve

Return a Promise instance with its state set to completed and its result passed in as the value of the Promise instance

var promise = Promise.resolve(123);
promise
    .then(function (val) {
        console.log('????? ', val);
    });
// 123 completed
Copy the code

Similarly, the promise.resolve argument can handle objects, functions, and so on in the same way as described in the specification above.

Promise.reject

Return an instance of Promise with its state set to rejected and also pass its result as the reason to the onRejected function

var promise = Promise.reject(123);
promise
    .then(null.function (val) {
        console.log('Rejected', val);
    });
// 123 has been rejected
Copy the code

Promise.all

Return a Promise instance that receives an array of multiple Promise instances. When all the promise instances are in the completed state, it goes to the completed state, otherwise it goes to the rejected state.

var promise1 = function () {
    return new Promise(function (resolve) {
        setTimeout(function () {
            console.log(1);
            resolve();
        }, 1000)}); }var promise2 = function () {
    return new Promise(function (resolve) {
        setTimeout(function () {
            console.log(2);
            resolve();
        }, 2000);
    });
}

Promise.all([promise1(), promise2()])
    .then(function () {
        console.log('All promises have been fulfilled');
    });
Copy the code

Note that multiple promises are made at the same time, i.e. in the example above, after waiting 1s to print 1, after waiting 1s to print 2 and ‘All promises are completed’.

Promise.race

Return a Promise instance and receive an array containing multiple Promise instances. When a Promise instance state changes, it goes into that state and cannot be changed. Here, all promise instances are competing, and only the first promise to enter the changed state is selected.

var promise1 = function () { return new Promise(function (resolve) { setTimeout(function () { console.log(1); resolve(1); }, 1000)}); } var promise2 = function () { return new Promise(function (resolve) { setTimeout(function () { console.log(2); resolve(2); }, 2000); }); } promise.race ([promise1(), promise2()]). Then (function (val) {console.log(' one Promise has changed: ', val); });Copy the code

Make a simple promise

Promise class core logic implementation

  1. A Promise is a class that, when instantiated, passes an executor function that executes immediately
  2. There are three states in Promise: success (depressing) Failure (Rejected) waiting (pending).
  3. When the resolve method is executed, the state changes from pending to depressing; when the reject method is executed, the state changes from pending to Rejected. Once the state is determined, it cannot be changed again
  4. The then method is defined in the prototype object and takes two parameters, a successful callback and a failed callback
  5. The then method determines the state. If the state is successful, a callback function is called (the value passed successfully), and if the state is unsuccessful, a callback function is called (the reason for the failure).
// The purpose of defining constants is: 1. To represent different states of promise 2. You can make the editor have code hints (haha)
const PENDING = 'pending'; / / wait for
const FULFILLED = 'fulfilled'; / / success
const REJECTED = 'rejected'; / / fail

class MyPromise {
    constructor(executor) {
        executor(this.resolve, this.reject)
    }
    status = PENDING // The instance attribute is used to indicate the promise state
    successData = undefined // Store the parameters that resolve passes after success
    failReason = undefined // Store the reject argument passed after a failure
    resolve = (val) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = FULFILLED // Change the status to success
        this.successData = val // Store the value after success
    }
    reject = (reason) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = REJECTED // Change the status to failed
        this.failReason = reason // Store the value after the failure
    }
    then(successCb, failCb) {
        // Determine the status
        if (this.status === FULFILLED) { // Synchronous call
            successCb(this.successData) // Call a successful callback to return the value
        } else if (this.status === REJECTED) { // Synchronous call
            failCb(this.failReason) // Call the failure callback to return the cause}}}Copy the code
const promise = new MyPromise((resolve, reject) = > {
    resolve('success')
    // reject('fail')
});

promise.then(val= > {
    console.log(val)
}, reason= > {
    console.log(reason)
})
Copy the code

Add asynchronous logic to the Promise

The above code is not handled asynchronously, which would be a problem if there was asynchronous logic

const PENDING = 'pending'; / / wait for
const FULFILLED = 'fulfilled'; / / success
const REJECTED = 'rejected'; / / fail

class MyPromise {
    constructor(executor) {
        executor(this.resolve, this.reject)
    }
    status = PENDING // The instance attribute is used to indicate the promise state
    successData = undefined // Store the parameters that resolve passes after success
    failReason = undefined // Store the reject argument passed after a failure
    successCallback = undefined // Store the successful callback function
    failCallback = undefined // Store the failed callback function
    resolve = (val) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = FULFILLED // Change the status to success
        this.successData = val // Store the value after success
        this.successCallback && this.successCallback(this.successData) // Asynchronous logic calls a successful callback
    }
    reject = (reason) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = REJECTED // Change the status to failed
        this.failReason = reason // Store the value after the failure
        this.failCallback && this.failCallback(this.failReason) // Asynchronous logic invokes the failed callback
    }
    then(successCb, failCb) {
        // Determine the status
        if (this.status === FULFILLED) { // Synchronous call
            successCb(this.successData) // Call a successful callback to return the value
        } else if (this.status === REJECTED) { // Synchronous call
            failCb(this.failReason) // Call the failure callback to return the cause
        } else { // Asynchronous invocation
            this.successCallback = successCb // Store a successful callback
            this.failCallback = failCb // Storage failed callback}}}Copy the code
const promise = new MyPromise((resolve, reject) = > {
    setTimeout(() = > {
        // resolve('success')
        reject('fail')},1500)}); promise.then(val= > {
    console.log(val)
}, reason= > {
    console.log(reason)
})
Copy the code

The implementation Promise then method is called multiple times

  1. Promise’s then method can be called multiple times.
  2. The following example has three then callbacks; if it is a synchronous callback, it simply returns the current value.
  3. If the callback is asynchronous, the successful and failed callbacks need to be saved with different values because they are not the same.
const PENDING = 'pending'; / / wait for
const FULFILLED = 'fulfilled'; / / success
const REJECTED = 'rejected'; / / fail

class MyPromise {
    constructor(executor) {
        executor(this.resolve, this.reject)
    }
    status = PENDING // The instance attribute is used to indicate the promise state
    successData = undefined // Store the parameters that resolve passes after success
    failReason = undefined // Store the reject argument passed after a failure
    successCallback = [] // Store the successful callback function
    failCallback = [] // Store the failed callback function
    resolve = (val) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = FULFILLED // Change the status to success
        this.successData = val // Store the value after success
        while(this.successCallback.length) { // Asynchronous logic calls a successful callback
            this.successCallback.shift()(this.successData)
        }
    }
    reject = (reason) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = REJECTED // Change the status to failed
        this.failReason = reason // Store the value after the failure
        while(this.failCallback.length) { // Asynchronous logic invokes the failed callback
            this.failCallback.shift()(this.failReason)
        }
    }
    then(successCb, failCb) {
        // Determine the status
        if (this.status === FULFILLED) { // Synchronous call
            successCb(this.successData) // Call a successful callback to return the value
        } else if (this.status === REJECTED) { // Synchronous call
            failCb(this.failReason) // Call the failure callback to return the cause
        } else { // Asynchronous invocation
            this.successCallback.push(successCb) // Store a successful callback
            this.failCallback.push(failCb) // Storage failed callback}}}Copy the code

Test examples:

const promise = new MyPromise((resolve, reject) = > {
    setTimeout(() = > {
        resolve('success')
        // reject('fail')
    }, 1500)}); promise.then(val= > {
    console.log(val, 11)},reason= > {
    console.log(reason, 11)
})

promise.then(val= > {
    console.log(val, 22)},reason= > {
    console.log(reason, 22)
})

promise.then(val= > {
    console.log(val, 33)},reason= > {
    console.log(reason, 33)})Copy the code

Chain calls to implement the Promise then method

  1. Promise’s then methods can be called chained, and the value of the subsequent then method’s callback parameter is the return value of the previous THEN method’s callback.
  2. The then method callback function parameter transfer includes two cases: 1. The parameter is the common value 2. The argument is another Promise object
const PENDING = 'pending'; / / wait for
const FULFILLED = 'fulfilled'; / / success
const REJECTED = 'rejected'; / / fail

class MyPromise {
    constructor(executor) {
        executor(this.resolve, this.reject)
    }
    status = PENDING // The instance attribute is used to indicate the promise state
    successData = undefined // Store the parameters that resolve passes after success
    failReason = undefined // Store the reject argument passed after a failure
    successCallback = [] // Store the successful callback function
    failCallback = [] // Store the failed callback function
    resolve = (val) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = FULFILLED // Change the status to success
        this.successData = val // Store the value after success
        while (this.successCallback.length) { // Asynchronous logic calls a successful callback
            this.successCallback.shift()(this.successData)
        }
    }
    reject = (reason) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = REJECTED // Change the status to failed
        this.failReason = reason // Store the value after the failure
        while (this.failCallback.length) { // Asynchronous logic invokes the failed callback
            this.failCallback.shift()(this.failReason)
        }
    }
    then(successCb, failCb) {
        const promise = new MyPromise((resolve, reject) = > {
            // Determine the status
            if (this.status === FULFILLED) { // Synchronous call
                const s = successCb(this.successData) // Call a successful callback to return the value
                // Determine whether p is a normal value or a promise object
                // If the value is normal, call resolve directly
                // If it is a Promise object, resolve or reject is called based on the result returned by the Promise object
                parsePromiseStatus(s, resolve, reject)
            } else if (this.status === REJECTED) { // Synchronous call
                const f = failCb(this.failReason) // Call the failure callback to return the cause
                parsePromiseStatus(f, resolve, reject)
            } else { // Asynchronous invocation
                this.successCallback.push(successCb) // Store a successful callback
                this.failCallback.push(failCb) // Storage failed callback}})return promise
    }
}
function parsePromiseStatus(p, resolve, reject) {
    if (p instanceof MyPromise) {
        p.then(resolve, reject)
    } else {
        resolve(p)
    }
}
Copy the code

Test examples:

const promise = new MyPromise((resolve, reject) = > {
    resolve('success') // So far, only synchronization is handled
});

function anther(n) {
    return new MyPromise((resolve, reject) = > {
        resolve('anther' + (n || ' '))})}const p = promise.then(val= > {
    console.log('val: ', val)
    // return '2222'
    return anther()
}, reason= > {
    console.log('reason', reason)
    return anther(2)
}).then(val= > {
    console.log('val2: ', val)
}, reason= > {
    console.log('reason2: ', reason)
})
Copy the code

Then method chain calls recognize the return promise object itself

const PENDING = 'pending'; / / wait for
const FULFILLED = 'fulfilled'; / / success
const REJECTED = 'rejected'; / / fail

class MyPromise {
    constructor(executor) {
        executor(this.resolve, this.reject)
    }
    status = PENDING // The instance attribute is used to indicate the promise state
    successData = undefined // Store the parameters that resolve passes after success
    failReason = undefined // Store the reject argument passed after a failure
    successCallback = [] // Store the successful callback function
    failCallback = [] // Store the failed callback function
    resolve = (val) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = FULFILLED // Change the status to success
        this.successData = val // Store the value after success
        while (this.successCallback.length) { // Asynchronous logic calls a successful callback
            this.successCallback.shift()(this.successData)
        }
    }
    reject = (reason) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = REJECTED // Change the status to failed
        this.failReason = reason // Store the value after the failure
        while (this.failCallback.length) { // Asynchronous logic invokes the failed callback
            this.failCallback.shift()(this.failReason)
        }
    }
    then(successCb, failCb) {
        const promise = new MyPromise((resolve, reject) = > {
            // Determine the status
            if (this.status === FULFILLED) { // Synchronous call
                setTimeout(() = > { // The reason for using setTimeout: If you do not use setTimeout, the method will not be executed and the return promise cannot be obtained
                    const p = successCb(this.successData) // Call a successful callback to return the value
                    // Determine whether p is a normal value or a promise object
                    // If the value is normal, call resolve directly
                    // If it is a Promise object, resolve or reject is called based on the result returned by the Promise object
                    parsePromiseStatus(promise, p, resolve, reject)
                }, 0)}else if (this.status === REJECTED) { // Synchronous call
                failCb(this.failReason) // Call the failure callback to return the cause
            } else { // Asynchronous invocation
                this.successCallback.push(successCb) // Store a successful callback
                this.failCallback.push(failCb) // Storage failed callback}})return promise
    }
}

function parsePromiseStatus(promise, p, resolve, reject) {
    if (promise === p) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}if (p instanceof MyPromise) {
        p.then(resolve, reject)
    } else {
        resolve(p)
    }
}
Copy the code

Test examples:

const promise = new MyPromise((resolve, reject) = > {
    resolve('success')});const p = promise.then(val= > {
    console.log(val, 'resolved')
    return p
}, reason= > {
    console.log(reason, 'rejected')
})

p.then(val= > {
    console.log(val, 22)},reason= > {
    console.log(reason.message)
})
Copy the code

Error trapping

It can be divided into two types: 1. Error capture of the actuator; 2. Error capture of the execution of then

const PENDING = 'pending'; / / wait for
const FULFILLED = 'fulfilled'; / / success
const REJECTED = 'rejected'; / / fail

class MyPromise {
    constructor(executor) {
        try {
            executor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
    }
    status = PENDING // The instance attribute is used to indicate the promise state
    successData = undefined // Store the parameters that resolve passes after success
    failReason = undefined // Store the reject argument passed after a failure
    successCallback = [] // Store the successful callback function
    failCallback = [] // Store the failed callback function
    resolve = (val) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = FULFILLED // Change the status to success
        this.successData = val // Store the value after success
        while (this.successCallback.length) { // Asynchronous logic calls a successful callback
            this.successCallback.shift()(this.successData)
        }
    }
    reject = (reason) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = REJECTED // Change the status to failed
        this.failReason = reason // Store the value after the failure
        while (this.failCallback.length) { // Asynchronous logic invokes the failed callback
            this.failCallback.shift()(this.failReason)
        }
    }
    then(successCb, failCb) {
        const promise = new MyPromise((resolve, reject) = > {
            // Determine the status
            if (this.status === FULFILLED) { // Synchronous call
                setTimeout(() = > {
                    try {
                        const p = successCb(this.successData)
                        parsePromiseStatus(promise, p, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)}else if (this.status === REJECTED) { // Synchronous call
                setTimeout(() = > {
                    try {
                        const p = failCb(this.failReason)
                        parsePromiseStatus(promise, p, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)}else { // Asynchronous invocation
                this.successCallback.push(successCb) // Store a successful callback
                this.failCallback.push(failCb) // Storage failed callback}})return promise
    }
}

function parsePromiseStatus(promise, p, resolve, reject) {
    if (promise === p) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}if (p instanceof MyPromise) {
        p.then(resolve, reject)
    } else {
        resolve(p)
    }
}
Copy the code

Test examples:

const promise = new MyPromise((resolve, reject) = > {
    reject('success')
    // throw new Error('executor error')
});

promise.then(val= > {
    console.log(val, 'resolved')},reason= > {
    console.log(reason, 'rejected')
    throw new Error('then method error')
}).then(val= > {
    console.log(val, 'then2')},reason= > {
    console.log(reason.message)
})
Copy the code

Chained invocation in asynchronous state — PROMISE calls resolve or reject asynchronously

const PENDING = 'pending'; / / wait for
const FULFILLED = 'fulfilled'; / / success
const REJECTED = 'rejected'; / / fail

class MyPromise {
    constructor(executor) {
        try {
            executor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
    }
    status = PENDING // The instance attribute is used to indicate the promise state
    successData = undefined // Store the parameters that resolve passes after success
    failReason = undefined // Store the reject argument passed after a failure
    successCallback = [] // Store the successful callback function
    failCallback = [] // Store the failed callback function
    resolve = (val) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = FULFILLED // Change the status to success
        this.successData = val // Store the value after success
        while (this.successCallback.length) { // Asynchronous logic calls a successful callback
            this.successCallback.shift()()
        }
    }
    reject = (reason) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = REJECTED // Change the status to failed
        this.failReason = reason // Store the value after the failure
        while (this.failCallback.length) { // Asynchronous logic invokes the failed callback
            this.failCallback.shift()()
        }
    }
    then(successCb, failCb) {
        const promise = new MyPromise((resolve, reject) = > {
            // Determine the status
            if (this.status === FULFILLED) { // Synchronous call
                setTimeout(() = > {
                    try {
                        const p = successCb(this.successData)
                        parsePromiseStatus(promise, p, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)}else if (this.status === REJECTED) { // Synchronous call
                setTimeout(() = > {
                    try {
                        const p = failCb(this.failReason)
                        parsePromiseStatus(promise, p, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)}else { // Asynchronous invocation
                this.successCallback.push(() = > {
                    setTimeout(() = > {
                        try {
                            const p = successCb(this.successData)
                            parsePromiseStatus(promise, p, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)})// Store a successful callback
                this.failCallback.push(() = > {
                    setTimeout(() = > {
                        try {
                            const p = failCb(this.failReason)
                            parsePromiseStatus(promise, p, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)})// Storage failed callback}})return promise
    }
}

function parsePromiseStatus(promise, p, resolve, reject) {
    if (promise === p) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}if (p instanceof MyPromise) {
        p.then(resolve, reject)
    } else {
        resolve(p)
    }
}

Copy the code

Test examples:

const promise = new MyPromise((resolve, reject) = > {
    setTimeout(() = > {
        resolve('success')},1000)}); promise.then(val= > {
    console.log(val, 'resolved')
    return 'xxx'
}, reason= > {
    console.log(reason, 'rejected')
}).then(val= > {
    console.log(val, 'then2')},reason= > {
    console.log(reason.message)
})
Copy the code

Makes the arguments of the then method optional

const PENDING = 'pending'; / / wait for
const FULFILLED = 'fulfilled'; / / success
const REJECTED = 'rejected'; / / fail

class MyPromise {
    constructor(executor) {
        try {
            executor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
    }
    status = PENDING // The instance attribute is used to indicate the promise state
    successData = undefined // Store the parameters that resolve passes after success
    failReason = undefined // Store the reject argument passed after a failure
    successCallback = [] // Store the successful callback function
    failCallback = [] // Store the failed callback function
    resolve = (val) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = FULFILLED // Change the status to success
        this.successData = val // Store the value after success
        while (this.successCallback.length) { // Asynchronous logic calls a successful callback
            this.successCallback.shift()()
        }
    }
    reject = (reason) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = REJECTED // Change the status to failed
        this.failReason = reason // Store the value after the failure
        while (this.failCallback.length) { // Asynchronous logic invokes the failed callback
            this.failCallback.shift()()
        }
    }
    then(successCb, failCb) {
        successCb = successCb || (value= > value) // Use a callback if there is a callback, assign a function if there is no callback
        failCb = failCb || (reason= > { throw reason })
        const promise = new MyPromise((resolve, reject) = > {
            // Determine the status
            if (this.status === FULFILLED) { // Synchronous call
                setTimeout(() = > {
                    try {
                        const p = successCb(this.successData)
                        parsePromiseStatus(promise, p, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)}else if (this.status === REJECTED) { // Synchronous call
                setTimeout(() = > {
                    try {
                        const p = failCb(this.failReason)
                        parsePromiseStatus(promise, p, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)}else { // Asynchronous invocation
                this.successCallback.push(() = > {
                    setTimeout(() = > {
                        try {
                            const p = successCb(this.successData)
                            parsePromiseStatus(promise, p, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)})// Store a successful callback
                this.failCallback.push(() = > {
                    setTimeout(() = > {
                        try {
                            const p = failCb(this.failReason)
                            parsePromiseStatus(promise, p, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)})// Storage failed callback}})return promise
    }
}

function parsePromiseStatus(promise, p, resolve, reject) {
    if (promise === p) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}if (p instanceof MyPromise) {
        p.then(resolve, reject)
    } else {
        resolve(p)
    }
}
Copy the code

Test examples:

const promise = new MyPromise((resolve, reject) = > {
        // resolve('success')
        reject('fail')}); promise.then().then().then(val= > {
    console.log('resolved: ', val)
}, reason= > {
    console.log('rejected: ', reason)
})
Copy the code

Promise.all implementation of the method

  1. The all method accepts an array, each element of which can be a normal value or a Promise object
  2. The order of the elements in the array is the order of the results
  3. If all values in the array are successful, then the function in the array is regarded as a success callback, and if one value is failed, then the function in the array is regarded as a failure callback
  4. The all method is called directly by the class, so all must be a static method of the class
const PENDING = 'pending'; / / wait for
const FULFILLED = 'fulfilled'; / / success
const REJECTED = 'rejected'; / / fail

class MyPromise {
    constructor(executor) {
        try {
            executor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
    }
    status = PENDING // The instance attribute is used to indicate the promise state
    successData = undefined // Store the parameters that resolve passes after success
    failReason = undefined // Store the reject argument passed after a failure
    successCallback = [] // Store the successful callback function
    failCallback = [] // Store the failed callback function
    resolve = (val) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = FULFILLED // Change the status to success
        this.successData = val // Store the value after success
        while (this.successCallback.length) { // Asynchronous logic calls a successful callback
            this.successCallback.shift()()
        }
    }
    reject = (reason) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = REJECTED // Change the status to failed
        this.failReason = reason // Store the value after the failure
        while (this.failCallback.length) { // Asynchronous logic invokes the failed callback
            this.failCallback.shift()()
        }
    }
    then(successCb, failCb) {
        successCb = successCb || (value= > value) // Use a callback if there is a callback, assign a function if there is no callback
        failCb = failCb || (reason= > { throw reason })
        const promise = new MyPromise((resolve, reject) = > {
            // Determine the status
            if (this.status === FULFILLED) { // Synchronous call
                setTimeout(() = > {
                    try {
                        const p = successCb(this.successData)
                        parsePromiseStatus(promise, p, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)}else if (this.status === REJECTED) { // Synchronous call
                setTimeout(() = > {
                    try {
                        const p = failCb(this.failReason)
                        parsePromiseStatus(promise, p, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)}else { // Asynchronous invocation
                this.successCallback.push(() = > {
                    setTimeout(() = > {
                        try {
                            const p = successCb(this.successData)
                            parsePromiseStatus(promise, p, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)})// Store a successful callback
                this.failCallback.push(() = > {
                    setTimeout(() = > {
                        try {
                            const p = failCb(this.failReason)
                            parsePromiseStatus(promise, p, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)})// Storage failed callback}})return promise
    }
    static all(arr) {
        const result = []
        let count = 0
        return new MyPromise((resolve, reject) = > {
            function addArrayEle(key, value) {
                result[key] = value
                count++
                // The length of the counter is the same as that of the array
                if (count === arr.length) {
                    resolve(result)
                }
            }
            for (let i = 0; i < arr.length; i++) {
                let current = arr[i]
                if (current instanceof MyPromise) {
                    / / promise object
                    current.then(value= > addArrayEle(i, value), reason= > reject(reason))
                } else {
                    / / common values
                    addArrayEle(i, current)
                }
            }
        })
    }
}

function parsePromiseStatus(promise, p, resolve, reject) {
    if (promise === p) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}if (p instanceof MyPromise) {
        p.then(resolve, reject)
    } else {
        resolve(p)
    }
}
Copy the code

Test examples:

function p1() {
    return new MyPromise((resolve, reject) = > {
        setTimeout(() = > {
            resolve('p1')},2000)})}function p2() {
    return new MyPromise((resolve, reject) = > {
        setTimeout(() = > {
            resolve('p2')},1000)
    })
}

MyPromise.all(['a'.'b', p1(), 'c', p2()]).then(val= > {
    console.log('val: ', val) // ["a", "b", "p1", "c", "p2"]
})
Copy the code

Implementation of the promise.race method

  1. The RACE method receives an array, and each element can be a normal value or a promise object
  2. If a common value exists, it is returned immediately. When all elements are Promise objects, if an instance state changes, it enters that state and cannot be changed
  3. Here, all promise instances are competing, and only the first promise to enter the changed state is selected, or the normal promise is selected
  4. The race method is called directly by the class, so all must be a static method of the class
const PENDING = 'pending'; / / wait for
const FULFILLED = 'fulfilled'; / / success
const REJECTED = 'rejected'; / / fail

class MyPromise {
    constructor(executor) {
        try {
            executor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
    }
    status = PENDING // The instance attribute is used to indicate the promise state
    successData = undefined // Store the parameters that resolve passes after success
    failReason = undefined // Store the reject argument passed after a failure
    successCallback = [] // Store the successful callback function
    failCallback = [] // Store the failed callback function
    resolve = (val) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = FULFILLED // Change the status to success
        this.successData = val // Store the value after success
        while (this.successCallback.length) { // Asynchronous logic calls a successful callback
            this.successCallback.shift()()
        }
    }
    reject = (reason) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = REJECTED // Change the status to failed
        this.failReason = reason // Store the value after the failure
        while (this.failCallback.length) { // Asynchronous logic invokes the failed callback
            this.failCallback.shift()()
        }
    }
    then(successCb, failCb) {
        successCb = successCb || (value= > value) // Use a callback if there is a callback, assign a function if there is no callback
        failCb = failCb || (reason= > { throw reason })
        const promise = new MyPromise((resolve, reject) = > {
            // Determine the status
            if (this.status === FULFILLED) { // Synchronous call
                setTimeout(() = > {
                    try {
                        const p = successCb(this.successData)
                        parsePromiseStatus(promise, p, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)}else if (this.status === REJECTED) { // Synchronous call
                setTimeout(() = > {
                    try {
                        const p = failCb(this.failReason)
                        parsePromiseStatus(promise, p, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)}else { // Asynchronous invocation
                this.successCallback.push(() = > {
                    setTimeout(() = > {
                        try {
                            const p = successCb(this.successData)
                            parsePromiseStatus(promise, p, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)})// Store a successful callback
                this.failCallback.push(() = > {
                    setTimeout(() = > {
                        try {
                            const p = failCb(this.failReason)
                            parsePromiseStatus(promise, p, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)})// Storage failed callback}})return promise
    }
    static all(arr) {
        const result = []
        let count = 0
        return new MyPromise((resolve, reject) = > {
            function addArrayEle(key, value) {
                result[key] = value
                count++
                // The length of the counter is the same as that of the array
                if (count === arr.length) {
                    resolve(result)
                }
            }
            for (let i = 0; i < arr.length; i++) {
                let current = arr[i]
                if (current instanceof MyPromise) {
                    / / promise object
                    current.then(value= > addArrayEle(i, value), reason= > reject(reason))
                } else {
                    / / common values
                    addArrayEle(i, current)
                }
            }
        })
    }
    static race(arr) {
        return new MyPromise((resolve, reject) = > {
            for (let i = 0; i < arr.length; i++) {
                let current = arr[i]
                if (current instanceof MyPromise) {
                    / / promise object
                    current.then(value= > resolve(value), reason= > reject(reason))
                } else {
                    resolve(current)
                }
            }
        })
    }
}

function parsePromiseStatus(promise, p, resolve, reject) {
    if (promise === p) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}if (p instanceof MyPromise) {
        p.then(resolve, reject)
    } else {
        resolve(p)
    }
}
Copy the code

Test examples:

function p1() {
    return new MyPromise((resolve, reject) = > {
        setTimeout(() = > {
            resolve('p1')},3000)})}function p2() {
    return new MyPromise((resolve, reject) = > {
        setTimeout(() = > {
            reject('p2')},1000)
    })
}

MyPromise.race(['b', p1(), 'a', p2()]).then(val= > {
    console.log('val: ', val); // val: b
}, reason= > {
    console.log('reason: ', reason)
})
Copy the code

Implementation of the promise.resolve method

  1. If the argument is a Promise object, it returns directly; If it is a value, a promise object is generated to return the value
  2. Resolve is a static method of the class
const PENDING = 'pending'; / / wait for
const FULFILLED = 'fulfilled'; / / success
const REJECTED = 'rejected'; / / fail

class MyPromise {
    constructor(executor) {
        try {
            executor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
    }
    status = PENDING // The instance attribute is used to indicate the promise state
    successData = undefined // Store the parameters that resolve passes after success
    failReason = undefined // Store the reject argument passed after a failure
    successCallback = [] // Store the successful callback function
    failCallback = [] // Store the failed callback function
    resolve = (val) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = FULFILLED // Change the status to success
        this.successData = val // Store the value after success
        while (this.successCallback.length) { // Asynchronous logic calls a successful callback
            this.successCallback.shift()()
        }
    }
    reject = (reason) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = REJECTED // Change the status to failed
        this.failReason = reason // Store the value after the failure
        while (this.failCallback.length) { // Asynchronous logic invokes the failed callback
            this.failCallback.shift()()
        }
    }
    then(successCb, failCb) {
        successCb = successCb || (value= > value) // Use a callback if there is a callback, assign a function if there is no callback
        failCb = failCb || (reason= > { throw reason })
        const promise = new MyPromise((resolve, reject) = > {
            // Determine the status
            if (this.status === FULFILLED) { // Synchronous call
                setTimeout(() = > {
                    try {
                        const p = successCb(this.successData)
                        parsePromiseStatus(promise, p, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)}else if (this.status === REJECTED) { // Synchronous call
                setTimeout(() = > {
                    try {
                        const p = failCb(this.failReason)
                        parsePromiseStatus(promise, p, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)}else { // Asynchronous invocation
                this.successCallback.push(() = > {
                    setTimeout(() = > {
                        try {
                            const p = successCb(this.successData)
                            parsePromiseStatus(promise, p, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)})// Store a successful callback
                this.failCallback.push(() = > {
                    setTimeout(() = > {
                        try {
                            const p = failCb(this.failReason)
                            parsePromiseStatus(promise, p, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)})// Storage failed callback}})return promise
    }
    static all(arr) {
        const result = []
        let count = 0
        return new MyPromise((resolve, reject) = > {
            function addArrayEle(key, value) {
                result[key] = value
                count++
                // The length of the counter is the same as that of the array
                if (count === arr.length) {
                    resolve(result)
                }
            }
            for (let i = 0; i < arr.length; i++) {
                let current = arr[i]
                if (current instanceof MyPromise) {
                    / / promise object
                    current.then(value= > addArrayEle(i, value), reason= > reject(reason))
                } else {
                    / / common values
                    addArrayEle(i, current)
                }
            }
        })
    }
    static race(arr) {
        return new MyPromise((resolve, reject) = > {
            for (let i = 0; i < arr.length; i++) {
                let current = arr[i]
                if (current instanceof MyPromise) {
                    / / promise object
                    current.then(value= > resolve(value), reason= > reject(reason))
                } else {
                    resolve(current)
                }
            }
        })
    }
    static resolve(value) {
        if(value instanceof MyPromise) {
            return value
        }
        return new MyPromise(resolve= > resolve(value))
    }
}

function parsePromiseStatus(promise, p, resolve, reject) {
    if (promise === p) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}if (p instanceof MyPromise) {
        p.then(resolve, reject)
    } else {
        resolve(p)
    }
}
Copy the code

Test examples:

function p1() {
    return new MyPromise((resolve, reject) = > {
        setTimeout(() = > {
            resolve('p1')},2000)
    })
}

MyPromise.resolve(2000).then(val= > {
    console.log('val: ', val) / / 2000
})

MyPromise.resolve(p1()).then(val= > {
    console.log('val: ', val) // 'p1'
})
Copy the code

Promise. Reject method implementation

  1. If the argument is a Promise object, it returns directly; If it is a value, a promise object is generated to return the value
  2. Resolve is a static method of the class
const PENDING = 'pending'; / / wait for
const FULFILLED = 'fulfilled'; / / success
const REJECTED = 'rejected'; / / fail

class MyPromise {
    constructor(executor) {
        try {
            executor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
    }
    status = PENDING // The instance attribute is used to indicate the promise state
    successData = undefined // Store the parameters that resolve passes after success
    failReason = undefined // Store the reject argument passed after a failure
    successCallback = [] // Store the successful callback function
    failCallback = [] // Store the failed callback function
    resolve = (val) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = FULFILLED // Change the status to success
        this.successData = val // Store the value after success
        while (this.successCallback.length) { // Asynchronous logic calls a successful callback
            this.successCallback.shift()()
        }
    }
    reject = (reason) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = REJECTED // Change the status to failed
        this.failReason = reason // Store the value after the failure
        while (this.failCallback.length) { // Asynchronous logic invokes the failed callback
            this.failCallback.shift()()
        }
    }
    then(successCb, failCb) {
        successCb = successCb || (value= > value) // Use a callback if there is a callback, assign a function if there is no callback
        failCb = failCb || (reason= > { throw reason })
        const promise = new MyPromise((resolve, reject) = > {
            // Determine the status
            if (this.status === FULFILLED) { // Synchronous call
                setTimeout(() = > {
                    try {
                        const p = successCb(this.successData)
                        parsePromiseStatus(promise, p, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)}else if (this.status === REJECTED) { // Synchronous call
                setTimeout(() = > {
                    try {
                        const p = failCb(this.failReason)
                        parsePromiseStatus(promise, p, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)}else { // Asynchronous invocation
                this.successCallback.push(() = > {
                    setTimeout(() = > {
                        try {
                            const p = successCb(this.successData)
                            parsePromiseStatus(promise, p, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)})// Store a successful callback
                this.failCallback.push(() = > {
                    setTimeout(() = > {
                        try {
                            const p = failCb(this.failReason)
                            parsePromiseStatus(promise, p, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)})// Storage failed callback}})return promise
    }
    static all(arr) {
        const result = []
        let count = 0
        return new MyPromise((resolve, reject) = > {
            function addArrayEle(key, value) {
                result[key] = value
                count++
                // The length of the counter is the same as that of the array
                if (count === arr.length) {
                    resolve(result)
                }
            }
            for (let i = 0; i < arr.length; i++) {
                let current = arr[i]
                if (current instanceof MyPromise) {
                    / / promise object
                    current.then(value= > addArrayEle(i, value), reason= > reject(reason))
                } else {
                    / / common values
                    addArrayEle(i, current)
                }
            }
        })
    }
    static race(arr) {
        return new MyPromise((resolve, reject) = > {
            for (let i = 0; i < arr.length; i++) {
                let current = arr[i]
                if (current instanceof MyPromise) {
                    / / promise object
                    current.then(value= > resolve(value), reason= > reject(reason))
                } else {
                    resolve(current)
                }
            }
        })
    }
    static resolve(value) {
        if (value instanceof MyPromise) {
            return value
        }
        return new MyPromise(resolve= > resolve(value))
    }
    static reject(reason) {
        return new MyPromise((resolve, reject) = > reject(reason))
    }
}

function parsePromiseStatus(promise, p, resolve, reject) {
    if (promise === p) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}if (p instanceof MyPromise) {
        p.then(resolve, reject)
    } else {
        resolve(p)
    }
}
Copy the code

Test examples:

MyPromise.reject(1234).then(null.val= > {
    console.log('val: ', val) // val: 123
})
Copy the code

Finally method implementation

  1. Used on prototype objects, so not static methods
  2. Finally executes whether the current state is successful or failed
  3. You can call the then method after the finally method to get the result
const PENDING = 'pending'; / / wait for
const FULFILLED = 'fulfilled'; / / success
const REJECTED = 'rejected'; / / fail

class MyPromise {
    constructor(executor) {
        try {
            executor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
    }
    status = PENDING // The instance attribute is used to indicate the promise state
    successData = undefined // Store the parameters that resolve passes after success
    failReason = undefined // Store the reject argument passed after a failure
    successCallback = [] // Store the successful callback function
    failCallback = [] // Store the failed callback function
    resolve = (val) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = FULFILLED // Change the status to success
        this.successData = val // Store the value after success
        while (this.successCallback.length) { // Asynchronous logic calls a successful callback
            this.successCallback.shift()()
        }
    }
    reject = (reason) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = REJECTED // Change the status to failed
        this.failReason = reason // Store the value after the failure
        while (this.failCallback.length) { // Asynchronous logic invokes the failed callback
            this.failCallback.shift()()
        }
    }
    then(successCb, failCb) {
        successCb = successCb || (value= > value) // Use a callback if there is a callback, assign a function if there is no callback
        failCb = failCb || (reason= > { throw reason })
        const promise = new MyPromise((resolve, reject) = > {
            // Determine the status
            if (this.status === FULFILLED) { // Synchronous call
                setTimeout(() = > {
                    try {
                        const p = successCb(this.successData)
                        parsePromiseStatus(promise, p, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)}else if (this.status === REJECTED) { // Synchronous call
                setTimeout(() = > {
                    try {
                        const p = failCb(this.failReason)
                        parsePromiseStatus(promise, p, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)}else { // Asynchronous invocation
                this.successCallback.push(() = > {
                    setTimeout(() = > {
                        try {
                            const p = successCb(this.successData)
                            parsePromiseStatus(promise, p, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)})// Store a successful callback
                this.failCallback.push(() = > {
                    setTimeout(() = > {
                        try {
                            const p = failCb(this.failReason)
                            parsePromiseStatus(promise, p, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)})// Storage failed callback}})return promise
    }
    finally(callback) {
        return this.then(value= > {
            return MyPromise.resolve(callback()).then(() = > value)
        }, reason= > {
            return MyPromise.resolve(callback()).then(() = > { throw reason })
        })
    }
    static all(arr) {
        const result = []
        let count = 0
        return new MyPromise((resolve, reject) = > {
            function addArrayEle(key, value) {
                result[key] = value
                count++
                // The length of the counter is the same as that of the array
                if (count === arr.length) {
                    resolve(result)
                }
            }
            for (let i = 0; i < arr.length; i++) {
                let current = arr[i]
                if (current instanceof MyPromise) {
                    / / promise object
                    current.then(value= > addArrayEle(i, value), reason= > reject(reason))
                } else {
                    / / common values
                    addArrayEle(i, current)
                }
            }
        })
    }
    static race(arr) {
        return new MyPromise((resolve, reject) = > {
            for (let i = 0; i < arr.length; i++) {
                let current = arr[i]
                if (current instanceof MyPromise) {
                    / / promise object
                    current.then(value= > resolve(value), reason= > reject(reason))
                } else {
                    resolve(current)
                }
            }
        })
    }
    static resolve(value) {
        if (value instanceof MyPromise) {
            return value
        }
        return new MyPromise(resolve= > resolve(value))
    }
    static reject(reason) {
        return new MyPromise((resolve, reject) = > reject(reason))
    }
}

function parsePromiseStatus(promise, p, resolve, reject) {
    if (promise === p) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}if (p instanceof MyPromise) {
        p.then(resolve, reject)
    } else {
        resolve(p)
    }
}
Copy the code

Test examples:

function p1() {
    return new MyPromise((resolve, reject) = > {
        setTimeout(() = > {
            resolve('p1')},2000)})}function p2() {
    return new MyPromise((resolve, reject) = > {
        reject('p1')
    })
}

p2().finally(() = > {
    console.log('finally')
    return p1()
}).then(val= > {
    console.log('resolve: ', val)
}, reason= > {
    console.log('reject: ', reason)
})
Copy the code

Implementation of the catch method

  1. Used on prototype objects, so not static methods
  2. Call the then method directly, passing undefined as the first argument and Reason as the second
const PENDING = 'pending'; / / wait for
const FULFILLED = 'fulfilled'; / / success
const REJECTED = 'rejected'; / / fail

class MyPromise {
    constructor(executor) {
        try {
            executor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
    }
    status = PENDING // The instance attribute is used to indicate the promise state
    successData = undefined // Store the parameters that resolve passes after success
    failReason = undefined // Store the reject argument passed after a failure
    successCallback = [] // Store the successful callback function
    failCallback = [] // Store the failed callback function
    resolve = (val) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = FULFILLED // Change the status to success
        this.successData = val // Store the value after success
        while (this.successCallback.length) { // Asynchronous logic calls a successful callback
            this.successCallback.shift()()
        }
    }
    reject = (reason) = > {
        if (this.status ! == PENDING)return // Execution continues only when the status is pending
        this.status = REJECTED // Change the status to failed
        this.failReason = reason // Store the value after the failure
        while (this.failCallback.length) { // Asynchronous logic invokes the failed callback
            this.failCallback.shift()()
        }
    }
    then(successCb, failCb) {
        successCb = successCb || (value= > value) // Use a callback if there is a callback, assign a function if there is no callback
        failCb = failCb || (reason= > { throw reason })
        const promise = new MyPromise((resolve, reject) = > {
            // Determine the status
            if (this.status === FULFILLED) { // Synchronous call
                setTimeout(() = > {
                    try {
                        const p = successCb(this.successData)
                        parsePromiseStatus(promise, p, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)}else if (this.status === REJECTED) { // Synchronous call
                setTimeout(() = > {
                    try {
                        const p = failCb(this.failReason)
                        parsePromiseStatus(promise, p, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)}else { // Asynchronous invocation
                this.successCallback.push(() = > {
                    setTimeout(() = > {
                        try {
                            const p = successCb(this.successData)
                            parsePromiseStatus(promise, p, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)})// Store a successful callback
                this.failCallback.push(() = > {
                    setTimeout(() = > {
                        try {
                            const p = failCb(this.failReason)
                            parsePromiseStatus(promise, p, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)})// Storage failed callback}})return promise
    }
    finally(callback) {
        return this.then(value= > {
            return MyPromise.resolve(callback()).then(() = > value)
        }, reason= > {
            return MyPromise.resolve(callback()).then(() = > { throw reason })
        })
    }
    catch(failCallback) {
        return this.then(undefined, failCallback);
    }
    static all(arr) {
        const result = []
        let count = 0
        return new MyPromise((resolve, reject) = > {
            function addArrayEle(key, value) {
                result[key] = value
                count++
                // The length of the counter is the same as that of the array
                if (count === arr.length) {
                    resolve(result)
                }
            }
            for (let i = 0; i < arr.length; i++) {
                let current = arr[i]
                if (current instanceof MyPromise) {
                    / / promise object
                    current.then(value= > addArrayEle(i, value), reason= > reject(reason))
                } else {
                    / / common values
                    addArrayEle(i, current)
                }
            }
        })
    }
    static race(arr) {
        return new MyPromise((resolve, reject) = > {
            for (let i = 0; i < arr.length; i++) {
                let current = arr[i]
                if (current instanceof MyPromise) {
                    / / promise object
                    current.then(value= > resolve(value), reason= > reject(reason))
                } else {
                    resolve(current)
                }
            }
        })
    }
    static resolve(value) {
        if (value instanceof MyPromise) {
            return value
        }
        return new MyPromise(resolve= > resolve(value))
    }
    static reject(reason) {
        return new MyPromise((resolve, reject) = > reject(reason))
    }
}

function parsePromiseStatus(promise, p, resolve, reject) {
    if (promise === p) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}if (p instanceof MyPromise) {
        p.then(resolve, reject)
    } else {
        resolve(p)
    }
}
Copy the code

Test examples:

function p2() {
    return new MyPromise((resolve, reject) = > {
        reject('p2 failed')
    })
}

p2().then(val= > {
    console.log('resolve: ', val)
}).catch(reason= > {
    console.log('fail: ', reason) // fail: p2 failed
})
Copy the code