In this article, I will step by step with you from scratch to achieve A Promise A+ specification of the Promise class, read this article carefully, and follow the steps to achieve once again, I believe you will have A deeper understanding of Promise. Here’s what you’ll learn.

  1. The emergence of promise, to solve the front-end problems
  2. Promise implementation mechanisms
  3. .then(...)..catch()
  4. Why can a promise be chain-executed.then().then().then()
  5. handwrittenPromise.race()andPromise.all()

The term

  1. A promise is an object or function that has then methods, and the behavior followsPromise A+Specification.
  2. Thenable is an object or function that has then methods.
  3. Value is the successful value of the promise state, which is also the resolve parameter. It can be a variety of data types, including undefined/thenable or promise
  4. Reason is the reject value when the promise state fails
  5. Exception is an exception thrown with a throw

specification

Promise States

Promise has three states, and pay attention to the flow between them

  1. pending

    1.1 Initial state, changeable

    1.2 A promise is in this state before resolve or Rejected

    1.3 Can be fulfilled through the resolve -> depressing state

    1.4 Reject -> Rejected can be configured

  2. fulfilled

    1.1 Final state, unchangeable

    1.2 A Promise is changed to this state by resolve

    1.3 Must have a value,

    (Note: if resolve() is used, the value is undefined.)

  3. rejected

    1.1 Final state, unchangeable

    1.2 A Promise is changed into this state after rejected

    1.3 Must have a reason value,

    (Note: If you reject(), the value is undefined.)

Tips: To summarize, promise state flow looks like this

pending -> resolve(value) -> fulfilled

pending -> reject(reason) -> rejected

Step by step, fulfill the promise

Step 1: Start with a class MyPromise

You definitely need a class first, because you usually use new Promse(…) when you use promises. So the first step is to declare a class, let’s call it MyPromise, and in the constructor, we have three properties, Status, Value, and Reason

// Declare state constants
const PENDING = 'pending' // Default state
const FULFILLED = 'fulfilled' // State after resolve()
const REJECTED = 'rejected' // reject(


class MyPromise {
    constructor() {
        this.status = PENDING // Default state
        this.value = null
        this.reason = null}}Copy the code

Step 2: The constructor adds parametersfn

New promise ((resolve, reject)=>{}) =>{})

class MyPromise {
    constructor(fn) {
        this.status = PENDING // Default state
        this.value = null
        this.reason = null
        // In the Promise A+ specification, we need to execute this function when we initialize A Promise, so we'll just do it
        fn()
    }
}
Copy the code

Fn (resolve, reject); fn (reject, reject); fn (resolve, reject, reject); fn (reject, reject); catch… Wrap it up and transform it as follows

class MyPromise {
    constructor(fn) {
        this.status = PENDING // Default state
        this.value = null
        this.reason = null
        /** * Note: This function is executed when a promise is initialized, and any error is thrown in reject * so try... catch... Wrap it up and use bind to bind the current this to prevent a mess */
        try {
            fn(this.resolve.bind(this), this.reject.bind(this))}catch (e) {
            this.reject(e) // Reject an error}}// State by pending-> depressing
    resolve(value) {}
    
    // The status is changed from pending-> Rejected
    reject(reason){}}Copy the code

Step 3:resolveandrejectmethods

Now that we’ve dealt with fn, we’ll focus on fn’s resolve and reject arguments, which are promise callbacks that change the state and return the result

class MyPromise {
    constructor(fn) {/ * *... * /}
    
    // The value argument is passed from the outside
     / * * *@description This is a big pity. There are two kinds of flow ways of the state, so only the current state can be changed when it is PENDING. Pity * pending -> resolve(value) -> reject(reason) -> reject * *@param {*} Value Specifies the value that can be of any type */
    resolve(value) {
        if (this.status === PENDING) {
            this.value = value
            this.status = FULFILLED
        }
    }
    // The reason argument is passed from the outside
    reject(reason) {
        if (this.status === PENDING) {
            this.reason = reason
           	this.status = REJECTED
        }
    }
    
	Return new Promise(resolve, reject)=>{* $.ajax(* url: 'xxx', * success: Function (data){function(data){function(data){function(data){function(data){function(data){ Resolve (data) *}, * error: function(error){reject(error) *}, * *) *} * */
}
Copy the code

Step 4:then(onFulfilled, onRejected)methods

Return new promise ().then(fn1,fn2). Then takes two arguments. The first argument is a callback on success and the second argument is a callback on failure. Only one of the two callbacks will be executed.

No matter how hard you try, you’re going to end up with the same thing, you’re only going to execute one of these callbacks, so let’s start with the then function, okay

class MyPromise {
    constructor(fn) {/ * *... * /}
    
    resolve(value) {/ * *... * /}
    reject(reason) {/ * *... * /}
    
    /** * this is a big pity, which is a big pity. /** * this is a big pity, which is a big pity, and the callback will be fulfilled once the * onRejected fails. * getData(). Then (* (value)=>{this.xxxData = value *}, * (reason)=>{ In this case, we usually make a popover to tell the user what is wrong */ / for example, network timeout, disconnection, unauthorized, etc. *} *) * * */
    then(onFulfilled, onRejected) {
        // After invoking then, the return value is still a promise, so we use a new promise2 to return it
        // .then().then().then().then().then().... As many as you want, endless chain operations
        const promise2 = new MyPromise((resolve, reject) = > { 
            switch (this.status) {
                // This is a big pity
                case FULFILLED:
                    onFulfilled()
                    break;

                // Call the failed callback when the state is REJECTED
                case REJECTED:
                    onRejected()
                    break;

                // What to do when the state is PENDING ????? We'll look at that next step
                case PENDING:
                    / /... The next step will be to study
                    break; }})return promise2
    }
}
Copy the code

Step 5: AnalyzepromiseTwo ways to call the

As we all know, when we use promises, there are two ways to invoke them

  • Call then directly on the declared Promise object. This chchained call automatically triggers a then callback when the result is resolved, so print out the resolve result (reject), for example

    const test = new Promise((resolve, reject) = >{
        setTimeout(() = >{
           resolve(111)},1000)
        // This is a big pity or Rejected function, which will automatically trigger the corresponding callback. Then you can operate the rest of our business logic in the callback function
    }).then((value) = >{
        console.log(value)
        console.log(test)
    })
    Copy the code
  • Method 2: If (‘ resolve ‘, ‘resolve’, ‘resolve’, ‘resolve’, ‘resolve’, ‘resolve’, ‘resolve’, ‘resolve’, ‘resolve’, ‘resolve’, ‘resolve’, ‘resolve’, ‘resolve’, ‘resolve’, ‘resolve’, ‘resolve’, ‘resolve’, ‘resolve’, ‘resolve’, ‘resolve’, ‘resolve’, ‘resolve’, ‘resolve’, ‘resolve’, ‘resolve’ How to handle pending state? (2) How to detect the state change and then trigger the corresponding callback function?

    const test = new Promise((resolve, reject) = >{
        setTimeout(() = >{
           resolve(111)},1000)
        // direct chain call, as soon as declared
    })
    
    test.then((value) = >{console.log(value)})
    test.then((value) = >{console.log(value)})
    test.then((value) = >{console.log(value)})
    test.then((value) = >{console.log(value)})
    
    console.log(test)
    Copy the code

Step 6: RewritethenMethod, usinggetterandsetterListening state changes

So to continue rewriting our code for the question in step 4, and the two questions considered in step 5 analysis,

  • For the first question in step 5, (1) how to handle pending state when calling then method?

    You can declare two arrays, store the callbacks that are not executed, and then pull them out of the array one by one when the state changes.

  • For the second question in step 5, (2) how to listen for state changes and then trigger the corresponding callback function of the then method?

    You can use getters and setters in ES6 to listen for changes in property values from constructor, declare getters and setters in the constructor display, and then pull the callback functions out of the cache callback array and execute them one by one. This corresponds exactly to method 2 in Step 5. When we call the THEN method synchronously, the same promise calls THEN several times synchronously, each time, the state is pending, then the callback function is stored in the cache array, when the state changes, the execution order is exactly the same as the order of registration

// Declare state constants
/ * *... * /

class MyPromise {
    // Declare two private arrays that cache callback functions whose state is not executed
    FULFILLED_CALLBACK_LIST = []
    REJECTED_CALLBACK_LIST = []
    /** * define a private variable _status, whose corresponding property is status in the constructor ** When we use getters and setters to listen for status, we manipulate _status * to prevent an infinite loop * (* if we don't store a private variable, Every time a status assignment and value is performed, * calls the setter and getter, so another wave of assignment and value operations, and then calls the setter and getter again, and so on, in an infinite loop. * * However, when the _status private variable member is used, the _status is not listened on by getters and setters, so the loop is avoided
    _status = PENDING

    constructor(fn) {/ * *... * /}
    resolve(value) {/ * *... * /}
    reject(reason) {/ * *... * /}

    /** * listen for the state change. If this becomes a pity or Rejected, * executes the callback next to the cached callback event array */
    set status(newStatus) {
        this._status = newStatus
        switch (newStatus) {
            case FULFILLED:
                this.FULFILLED_CALLBACK_LIST.forEach(callback= > {
                    callback(this.value)
                });
                break;
            case REJECTED:
                this.REJECTED_CALLBACK_LIST.forEach(callback= > {
                    callback(this.reason)
                });
                break; }}// 
    get status() {
        return this._status
    }

    then(onFulfilled, onRejected) {
        const promise2 = new MyPromise((resolve, reject) = > {
            switch (this.status) {
                / * *... * /
                case PENDING:
                    // When the state is PENDING, we store the callback and execute it when the state changes
                    this.FULFILLED_CALLBACK_LIST.push(onFulfilled)
                    this.REJECTED_CALLBACK_LIST.push(onRejected)
                    break; }})return promise2
    }
}
Copy the code

Step 7:thenMethod parameter verification,queueMicrotaskWrap callback method to switch to microtask queue

Next, we’ll refine the THEN method, starting with three areas

  • (1)then(onFulfilled, onRejected).thenIn the methodonFulfilledandonRejectedIt’s received from the outside, it needs to validate parameters, and it’s expected to be a function
  • (2)onFulfilled, onRejectedThe execution of the function is required in the microtask, which is simply used herequeueMicrotaskSo let’s wrap up the function
  • (3)onFulfilled, onRejectedDuring the execution of the function, errors may be reported. After all, we do not know what business will be executed in the external function. It is normal to report errors, so it needs to be usedtry... catch...Wrap it up, and if there is an error, send it directlyreject(e)To go out
// Declare state constants
/ * *... * /
class MyPromise {
    / * *... * /
    constructor(fn) {/ * *... * /}
    resolve(value) {/ * *... * /}
    reject(reason) {/ * *... * /}
    get status() {/ * *... * /}
    set status(newStatus) {/ * *... * /}

    then(onFulfilled, onRejected) {
        /** * this is a pity, onFulfilled */ this is a pity, onFulfilled */ this is a pity, onFulfilled */ This is a pity, onFulfilled */ this is a pity, onFulfilled */ this is a pity, onFulfilled */
        const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) = > {
            // The default function given should have a return value. Why? The analysis is given in the following steps
            return value
        }
        const realOnRejected = this.isFunction(onRejected) ? onRejected : (reason) = > {
            throw reason
        }

        const promise2 = new MyPromise((resolve, reject) = > {
            // Create a microtask using queueMicrotask
            const fulfilledMicrotask = () = > {
                queueMicrotask(() = > {
                    // Use catch wrap, reject if something goes wrong
                    try {
                        realOnFulfilled(this.value)
                    } catch (e) {
                        reject(e)
                    }
                })
            }

            // Create a microtask using queueMicrotask
            const rejectedMicrotask = () = > {
                queueMicrotask(() = > {
                    // Use catch wrap, reject if something goes wrong
                    try {
                        realOnRejected(this.reason)
                    } catch (e) {
                        reject(e)
                    }
                })
            }

            switch (this.status) {
                case FULFILLED:
                    // Call the corresponding microtask instead
                    fulfilledMicrotask()
                    break;
                case REJECTED:
                    // Call the corresponding microtask instead
                    rejectedMicrotask()
                    break;
                case PENDING:
                    // Cache array is used to store microtasks
                    this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask)
                    this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask)
                    break; }})return promise2
    }

    // Check whether the argument is a function
    isFunction(param) {
        return typeof param === 'function'}}Copy the code

Step 8: Analyzethen(onFulfilled, onRejected)Method to call back the return value of the method, throw a question, and declareresolvePromise(promise2, x, resolve, reject)methods

There is another important point to consider. When calling the then(onFulfilled, onRejected) method, the external function onFulfilled, onFulfilled, may have a return value. And there are many different types of return values, so just to throw out a few puzzles,

  • (1) If the return value is still onepromiseHow to deal with it?
  • (2) What if the return value is an object or function?
  • (3) The return value is notpromiseIs not an object or a function, so it has to be a primitive type.

In this step we first no matter how to deal with, we first throw out the problem, write a processing method to deal with, in the ninth step to concentrate on how to deal with these several cases, so then the seventh step code rewrite

// Declare state constants
/ * *... * /

class MyPromise {
    / * *... * /
    constructor(fn) {/ * *... * /}
    resolve(value) {/ * *... * /}
    reject(reason) {/ * *... * /}
    get status() {/ * *... * /}
    set status(newStatus) {/ * *... * /}
    
    then(onFulfilled, onRejected) {
        / * *... * /
        const promise2 = new MyPromise((resolve, reject) = > {
            const fulfilledMicrotask = () = > {
                queueMicrotask(() = > {
                    try {
                        // The return value is defined as x
                        const x = realOnFulfilled(this.value)
                        /** * The method used to handle the return value is defined as resolvePromise ** Why does the parameter pass the current return value promise2? * This is because the return value x is also a Promise, then the return value of promise2 is also passed, to verify that x === promise2, * if it is exactly the same Promise, it will cause an infinite loop * */
                        this.resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            const rejectedMicrotask = () = > {
                queueMicrotask(() = > {
                    try {
                        // The return value is defined as x
                        const x = realOnRejected(this.reason)
                        // Process the result
                        this.resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
         	/ * *... * /
        })
        return promise2
    }

    // There are a lot of if operations to handle the return value in the callback function, because there are a number of cases that should be considered
    resolvePromise(promise2, x, resolve, reject){}isFunction(param) {/ * *... * /}}Copy the code

Step 9: AnatomyresolvePromise(promise2, x, resolve, reject)methods

Then, the doubts thrown in step 8 are improved, and the judgment logical framework of resolvePromise(promise2, X, Resolve, Reject) method is perfected. Step by step, the analysis shows that there are four if judgments outside

  • Promise2 === x (promise2 == x, promise2 == x, promise2 == x, promise2 == x) Because follow Promise A + specification of chain operation. Then (). Then (). Then (). Then ()… Each time. Then () returns a new Promise object, if a callback is called, for example

    let test = new Promise((resolve, reject) = >{
        resolve(111)
    }).then(
    	() = >{
            // Suppose that after executing this callback, the return value x is the same promise as after executing.then(), due to some unexpected operation
            // return promise2})Copy the code

    This would result in an infinite loop, because if it were equal it would be treated as if the return value was a promise, and its then() method would continue to recursively parse the return value, but every time the x returned was the same promise, it would result in an infinite loop, so at the initial stage, you need to decide, If x === promise2, throw a type exception.

  • (2) If x instanceof MyPromise is a promise, the return value x is still a promise. Then () returns a promise that inherits the state and result of x

    let test = new Promise((resolve, reject) = >{
        resolve(111)
    }).then(
    	(value) = >{
            console.log(value)
            // Suppose that after executing this callback, a Promise is returned
            // 
            return new Promise((resolve, reject) = >{
                resolve(222)})})Copy the code

  • (3) If the return value of the callback x is an object or method, as in the following example

    let test = new Promise((resolve, reject) = >{
        resolve(111)
    }).then(
    	(value) = >{
            console.log(value)
            // Suppose an object is returned after executing the callback
            // 
            return {
                a: 'aaa'.b: 'bbb'
            }
        }
    )
    test.then(console.log)
    Copy the code
    let test = new Promise((resolve, reject) = >{
        resolve(111)
    }).then(
    	(value) = >{
            console.log(value)
            // Suppose an object is returned after executing the callback
            // 
            return {
                a: 'aaa'.b: 'bbb'.then: 'ccc'
            }
        }
    )
    test.then(console.log)
    Copy the code
    let test = new Promise((resolve, reject) = >{
        resolve(111)
    }).then(
    	(value) = >{
            console.log(value)
            // Suppose an object is returned after executing the callback
            // 
            return {
                a: 'aaa'.b: 'bbb'.then: (resolve, reject) = >{
                    resolve(2222)
                }
            }
        }
    )
    test.then(console.log)
    Copy the code

Js let test = new Promise((resolve, reject)=>{resolve(111)}).then((value)=>{console.log(value) // return ()=>{console.log(2222)}}) test.then(console.log) {console.log(2222)}}) 'then((resolve, reject)=>{resolve(1111)})', 'resolve(reject)=>{resolve(1111)})', 'resolve(reject)=>{resolve(1111)})'  It's a little bit convoluted here, but I'll just look at the code and try to understand it, so make sure you type the example.Copy the code
  • (4) Exclude the above conditions, the remaining can only be the basic type, if the basic data type, directly return the result

Step 10: ImplementresolvePromise(promise2, x, resolve, reject)Method to parse the result returned by the callback functionx

ResolvePromise (promise2, x, resolve, reject)

// Declare state constants
/ * *... * /

class MyPromise {
    / * *... * /

    constructor(fn) {/ * *... * /}
    resolve(value) {/ * *... * /}
    reject(reason) {/ * *... * /}
    get status() {/ * *... * /}
    set status(newStatus) {/ * *... * /}

    then(onFulfilled, onRejected) {
        / * *... * /
        const promise2 = new MyPromise((resolve, reject) = > {
            const fulfilledMicrotask = () = > {
                queueMicrotask(() = > {
                    try {
                        // The return value is defined as x
                        const x = realOnFulfilled(this.value)
                        this.resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            const rejectedMicrotask = () = > {
                queueMicrotask(() = > {
                    try {
                        // The return value is defined as x
                        const x = realOnRejected(this.reason)
                        // Process the result
                        this.resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
         	/ * *... * /
        })
        return promise2
    }

    // There are a lot of if operations to handle the return value in the callback function, because there are a number of cases that should be considered
    resolvePromise(promise2, x, resolve, reject) {
        If.then() returns the same promise as the return value of the callback
        if (x === promise2) {
            // Cannot be equal. If equality is an infinite loop, the uniqueness of the promise returned after each execution should be guaranteed
            return reject(new TypeError('The promise and the return value are the same'))}// if x is a promise
        if (x instanceof MyPromise) {
            // Continue parsing Promise in microtask
            queueMicrotask(() = > {
                x.then(
                    (y) = > {
                        // Recursive parsing until other cases are resolved
                        this.resolvePromise(promise2, y, resolve, reject)
                    },
                    reject,
                )
            })
        } else if (typeof x === 'object' || this.isFunction(x)) { // if the return value is an object or a function
            // Check whether it is null
            if (x === null) {
                return resolve(x) // Return the result directly
            }

            // See if there are any then methods in the object that match the Promise specification
            let then = null
            try {
                // Select 'then', reject 'then', reject 'then
                then = x.then
            } catch (e) {
                reject(e)
            }

            if (this.isFunction(then)) {
                // Only one successful and failed callback can be called
                let called = false
                // Wrap a try.. catch... Catch an exception in a method
                try {
                    // if x. teng is function
                    then.call(
                        x,
                        (y) = > {
                            if (called) return
                            called = true
                            // Continue parsing the returned result
                            this.resolvePromise(promise2, y, resolve, reject)
                        },
                        (r) = > {
                            if (called) return
                            called = true
                            // reject it
                            reject(r)
                        }
                    )
                } catch (e) {
                    if (called) return
                    called = true
                    // reject it
                    reject(r)
                }
            } else { / / x is an object, and x.t hen not function | | x is a function directly
                resolve(x) // Resolve}}else { // Select * from 'resolve'; // select * from 'resolve'
            resolve(x)
        }
    }

    isFunction(param) {/ * *... * /}}Copy the code

Step 11: Implementation.catch()methods

In addition to the. Then (ondepressing, onRejected) method’s second parameter, a promise can also be obtained with the. Catch () method, such as

const test = new Promise((resolve, reject) = >{
    setTimeout(() = >{
       reject(111)},1000)
}).then(
    value= >{},
    reason= >{
        console.log(reason, 'onRejected')})/ / or
test.catch(reason= >{
    console.log(reason, 'onRejected')})Copy the code

We’ll implement one ourselves

// Declare state constants
/ * *... * /

class MyPromise {
    / * *... * /

    constructor(fn) {/ * *... * /}
    resolve(value) {/ * *... * /}
    reject(reason) {/ * *... * /}
    get status() {/ * *... * /}
    set status(newStatus) {/ * *... * /}
    
    // Call the then method to pass only unsuccessful callbacks
    catch(onRejected) {
        return this.then(null, onRejected)
    }

    then(onFulfilled, onRejected) {/ * *... * /}

    resolvePromise(promise2, x, resolve, reject) {/ * *... * /}

    isFunction(param) {/ * *... * /}}Copy the code

With the 10 steps above, we can now use our own defined promises, which we can plug into use cases to try out, for example

const test = new MyPromise((resolve, reject) = >{
    setTimeout(() = > {
        resolve(111)},1000)
}).then(console.log)

console.log(test)
Copy the code

or

const test = new MyPromise((resolve, reject) = >{
    setTimeout(() = > {
        resolve(111)},1000)
})

test.then(console.log)
test.then(console.log)
test.then(console.log)
test.then(console.log)

Copy the code

Step 12:Promise.resolve()

In practice, we sometimes use the promise static method promise.resolve (value) to return a promise object directly. There are two cases

  • (1) If value is a primitive type, return a Promise object with that primitive type as value

    const test = Promise.resolve(1111)
    test.then(console.log)
    Copy the code
  • (2) If value is a Promise object, return the promise

    const test = Promise.resolve( 
    	new Promise((resolve, reject) = >{
            setTimeout(() = >{
                resolve(111)},1000)
        })
    )
    test.then(console.log)
    Copy the code

We implement a myPromise.resolve () ourselves

// Declare state constants
/ * *... * /

class MyPromise {
    / * *... * /
    constructor(fn) {/ * *... * /}
    
    /** * Note: static member methods can only be called from the class name * and only static methods can be called from the method body
    static resolve(value) {
        // If value is a Promise, return it
        if (value instanceof MyPromise) {
            return value
        }

        // If not, return a new Promise with value as the value
        return new MyPromise((resolve) = > {
            resolve(value)
        })
    }

    resolve(value) {/ * *... * /}
    reject(reason) {/ * *... * /}
    get status() {/ * *... * /}
    set status(newStatus) {/ * *... * /}
    then(onFulfilled, onRejected) {/ * *... * /}
    resolvePromise(promise2, x, resolve, reject) {/ * *... * /}
    isFunction(param) {/ * *... * /}}Copy the code

Step 13:Promise.reject()

Promise. Reject (reason) directly returns a fulfilled Promise, for example

const test = Promise.reject(1111)
test.then(
	(value) = >{console.log(value,'fulfilled')},
    (reason) = >{console.log(reason,'rejected')},Copy the code

Implement one yourself

// Declare state constants
/ * *... * /

class MyPromise {
    / * *... * /
    constructor(fn) {/ * *... * /}
    static resolve(value) {/ * *... * /}
    
    static reject(reason) {
        // Return a new Promise with reason
        return new MyPromise((resolve, reject) = > {
            reject(reason)
        })
    }

    resolve(value) {/ * *... * /}
    reject(reason) {/ * *... * /}
    get status() {/ * *... * /}
    set status(newStatus) {/ * *... * /}
    then(onFulfilled, onRejected) {/ * *... * /}
    resolvePromise(promise2, x, resolve, reject) {/ * *... * /}
    isFunction(param) {/ * *... * /}}Copy the code

Step 14:Promise.race()

The promise’s race method passes in an array (actually an array of classes, as long as it can be iterated) that contains a bunch of promise objects waiting to be executed. If they are not Promises, they will be internally converted to all promises. We don’t know the result of the promie’s final execution. When promise.race (arr).then() is used, once one of these promises is resolved, the result is returned directly, and the execution is synchronous, which can be simulated using a for loop, which defaults to synchronous execution

// Declare state constants
/ * *... * /

class MyPromise {
    / * *... * /
    constructor(fn) {/ * *... * /}
    static resolve(value) {/ * *... * /}
    static reject(reason) {/ * *... * /}
    
    static race(iterableList) {
        return new MyPromise((resolve, reject) = > {
            /** * Determines whether the passed argument is iterable ** /
            if(! MyPromise.isIterable(iterableList)) {return reject(new TypeError(`${iterableList} is not iterable (cannot read property Symbol(Symbol.iterator))`))}// 2. Convert an array to an array
            const promiseList = Array.from(iterableList)
            const promiseLength = promiseList.length

            // Resolve an empty array
            if (promiseLength === 0) {
                return resolve([])
            } else {
                // make a Promise
                for (let i = 0; i < promiseLength; i++) {
                    // If one of the parameters is not a Promise, convert all of them
                    MyPromise.resolve(promiseList[i]).then(
                        (value) = > {
                            // 5, return the result directly
                            return resolve(value)
                        },
                        (reason) = > {
                            / / 5,
                            return reject(reason)
                        }
                    )
                }
            }
        })
    }

    / * * *@description Check whether value is iterable *@param {*} value 
     * @returns {Boolean} True: iterable; False: cannot iterate */
    static isIterable(value) {
        // Return false if null or undefined
        if (value === null || value === undefined) {
            return false
        } else {
            Iterator is not iterated by default if an object does not contain symbol. iterator
            Iterable objects implement the symbol. iterator iterator by default
            return! (value[Symbol.iterator] === undefined)}}resolve(value) {/ * *... * /}
    reject(reason) {/ * *... * /}
    get status() {/ * *... * /}
    set status(newStatus) {/ * *... * /}
    then(onFulfilled, onRejected) {/ * *... * /}
    resolvePromise(promise2, x, resolve, reject) {/ * *... * /}
    isFunction(param) {/ * *... * /}}Copy the code

Step 15:Promise.all()

The difference between promise’s all method and race method is that all returns only if all results are resolve, and once there is a Reject, the failed callback is executed

// Declare state constants
/ * *... * /

class MyPromise {
    / * *... * /
    constructor(fn) {/ * *... * /}
    static resolve(value) {/ * *... * /}
    static reject(reason) {/ * *... * /}
   
    static race(iterableList) {/ * *... * /}
    
    static all(iterableList) {
        return new MyPromise((resolve, reject) = > {
            // 1. Check whether the parameter is iterable
            if(! MyPromise.isIterable(iterableList)) {return reject(new TypeError(`${iterableList} is not iterable (cannot read property Symbol(Symbol.iterator))`))}// 2, convert to array
            const promiseList = Array.from(iterableList)
            const promiseLength = promiseList.length
            /** * Take a counter, each time the resolve callback is executed +1 * and declare an array of returned values. Each time the resolve callback is executed, the returned values will be stored. * Then the array resolve of the success result is exported */
            let resolvedCount = 0
            let resolvedValues = new Array(promiseLength)
            // If it is an empty array, return an empty array
            if (promiseLength === 0) {
                return resolve([])
            } else {
                // 3
                for (let i = 0; i < promiseLength; i++) {
                    // Convert all to Promise objects
                    MyPromise.resolve(promiseList[i]).then(
                        (value) = > {
                            +1 on success
                            resolvedCount++
                            // add the corresponding value to the result array
                            resolvedValues[i] = value
                            if (resolvedCount === promiseLength) {
                                return resolve(resolvedValues)
                            }
                        },
                        (reason) = > {
                            If there is a failure, reject the failure result
                            return reject(reason)
                        },
                    )

                }
            }
        })
    }
    
    static isIterable(value) {/ * *... * /}


    resolve(value) {/ * *... * /}
    reject(reason) {/ * *... * /}
    get status() {/ * *... * /}
    set status(newStatus) {/ * *... * /}
    then(onFulfilled, onRejected) {/ * *... * /}
    resolvePromise(promise2, x, resolve, reject) {/ * *... * /}
    isFunction(param) {/ * *... * /}}Copy the code

conclusion

The above realized a promise of their own, you can try to knock, substitute in some use cases to try, a total of the following promise

  1. new Promise()What did he do
  2. thenThe execution mechanism within a method
  3. The principle of the promise chain execution
  4. .catchMethod Execution Principle
  5. handwrittenpromise.race()
  6. handwrittenpromise.all()

Source code address: gitee.com/hrbust_chen…