Step by step, teach you how to write promises

The main purpose of writing this article is to consolidate their knowledge and a deeper understanding of the principle of promise, there are some unfriendly for beginners, it is suggested that beginners go to understand the basic use of promise and then look at this article. Then, all, catch, race, Promise. Resolve, Promise. Reject. Mainly look at handwritten ideas, there is a general idea is good, not really ask you to write a Promise out, focus on the all and race method to achieve the following, the interview may test!

Step 1: initial structure setup

As we all know, Promise has three states in total, namely pending, depressing and reject. The three states are irreversible, and the states can be changed by calling resolve, reject and throwing errors. This can only be pending–> depressing or pending–> Reject. We throw uses try catch to catch error information

function Promise(executor){
    // Add the attribute. The initial state is pending
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    // Save the instance object's this value
    const self = this;
    / / resolve function
    function resolve(data){
        If the state is not pending, the code below cannot execute a direct return
        if(self.PromiseState ! = ='pending') return
        // Modify object state (promiseState)
        self.PromiseState = 'fulfilled';
        // Set object result value (promiseResult)
        self.PromiseResult = data;
    }
    / / reject function
    function reject(data){
        If the state is not pending, the code below cannot execute a direct return
        if(self.PromiseState ! = ='pending') return
        // Modify object state (promiseState)
        self.PromiseState = 'rejectd';
        // Set object result value (promiseResult)
        self.PromiseResult = data;
    }
    try{
    Resolve and reject are called synchronously to the executor function
    executor(resolve,reject);
    }catch(e){
        // Reject is rejected if an error is thrownreject(e); }}// Add then methods to the prototype so that instance objects can use then methods
Promise.prototype.then = function(onResolved,onRejected){}Copy the code
  • So at the beginning of the structure almost built up, you can go to verify

Step 2: then method

1. The then method performs the callback

Promise. Then two callback functions will be passed, one is a pity callback function and the other is a reject callback function which is fulfilled

Promise.prototype.then = function (onResolved, onRejected) {
    if (this.PromiseState === 'fulfilled') {
        onResolved(this.PromiseResult);
    }
    if (this.PromiseState === 'rejected') {
        onRejected(this.PromiseResult); }}Copy the code

The result is shown below:

Of course, it’s not the final version and we’ll be working on it, so keep reading

2. Execute asynchronous task callback

Now let’s say we have a scenario where asynchronous task callbacks are executed and the state changes after a few seconds. How would that work? So we need to keep improving our code so that asynchronous task callbacks can be executed.

let p = new Promise((resolve, reject) = > {
    setTimeout(() = > {
        resolve('123');
    }, 1000)
})
p.then(value= > {
    console.log(value);
}, reason= > {
    console.warn(reason);
})
Copy the code

The answer is: no output, no execution. It takes a second to change the state, so when we enter the then callback it starts off in a pending state and our code doesn’t have a function to determine that state.

1. So we need to perfect and judge the case when the initial state is pending.

2. When the state is pending, how can we execute the callback function when the state changes? If the state does not change, we cannot execute the callback function, and then call the saved callback function when the state changes. We create a callback property to hold the callback function (+++ indicates new code)

3.4. After the callback function is saved, when should we call it? We need to call onResolved and onRejected functions after the state changes, so we can find resolve and Reject to write and determine whether there is a callback function in callback. If so, the function is called after the state

function Promise(executor) {
    // Add the attribute. The initial state is pending
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    // Declare the property save callback function
    this.callback = {};  / / 2. + + + + + + + + + + + + + + + +
    // Save the instance object's this value
    const self = this;
    / / resolve function
    function resolve(data) {
        // Determine the state (state reversible)
        if(self.PromiseState ! = ='pending') return
        // Modify object state (promiseState)
        self.PromiseState = 'fulfilled';
        // Set object result value (promiseResult)
        self.PromiseResult = data;
        // Call the successful callback 3.++++++++++++
        if(self.callback.onResolved){ self.callback.onResolved(); }}/ / reject function
    function reject(data) {
        // Determine the state (state reversible)
        if(self.PromiseState ! = ='pending') return
        // Modify object state (promiseState)
        self.PromiseState = 'rejected';
        // Set object result value (promiseResult)
        self.PromiseResult = data;
        // Call the failed callback 4.++++++++
        if(self.callback.onRejected){ self.callback.onRejected(); }}try {
        Resolve and reject are called synchronously to the executor function
        executor(resolve, reject);
    } catch(e) { reject(e); }}Copy the code

Of course, our then method also needs to be updated

Promise.prototype.then = function (onResolved, onRejected) {
    // Success status
    if (this.PromiseState === 'fulfilled') {
        onResolved(this.PromiseResult);
    }
    // Failed state
    if (this.PromiseState === 'rejected') {
        onRejected(this.PromiseResult);
    }
    // Asynchronous state processing
    if(this.PromiseState === 'pending') {/ / 1. + + + + + + + + + +
        // Save the callback function
		this.callback = {    
            onResolved,  // Short for key value and key name equality
            onRejected
        }
    }   //+++++++
}
Copy the code

Now print p and look at it and there’s a callback property

Now our then method can asynchronously execute the task callback by executing the setTimeout line above on the console and printing the result after one second, as shown in the figure below

Attention!!!!!

If we wait for one second to output the result and then click on the promise object, the state will become a pity. If we click on the promise object before output the result, the state will still be pending

3. Specify multiple callback functions

What happens when we execute the following code

let p = new Promise((resolve, reject) = > {
    setTimeout(() = > {
        resolve('123');
    }, 1000);
})
p.then(value= > {
    console.log(value);
}, reason= > {
    console.warn(reason);
})
p.then(value= > {
    alert(value);
}, reason= > {
    alert(reason);
})
console.log(p)
Copy the code

With the built-in Promise it prints 123 and then pops up an Alert 123, but what happens with the Promise we just wrote? The answer is that it’s just going to pop up an Alert 123, so why is that? Since the latter callback overwrites the previous console.log callback and has only one popover output, we need to make code improvements

1. We’re going to change the way we save callbacks by declaring a callbacks array to save callbacks. In the previous step, we saved with objects.

2. Then push each callback function as an object into the Callbacks array

3. The array stores multiple objects. Each object has the onResolved and onRejected callback functions

function Promise(executor) {
    // Add the attribute. The initial state is pending
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    // Declare attributes (using arrays instead) 1.+++++++++
    this.callbacks= [];
    // Save the instance object's this value
    const self = this;
    / / resolve function
    function resolve(data) {
        // Determine the state (state reversible)
        if(self.PromiseState ! = ='pending') return
        // Modify object state (promiseState)
        self.PromiseState = 'fulfilled';
        // Set object result value (promiseResult)
        self.PromiseResult = data;
        // Call the successful callback 3.+++++++
        self.callbacks.forEach(item= > {
            item.onResolved(data)
        })
    }
    / / reject function
    function reject(data) {
        // Determine the state (state reversible)
        if(self.PromiseState ! = ='pending') return
        // Modify object state (promiseState)
        self.PromiseState = 'rejected';
        // Set object result value (promiseResult)
        self.PromiseResult = data;
        // Call the failed callback function
        self.callbacks.forEach(item= > { / / 4. + + + + + + + +
            item.onRejected(data)
        })
    }
    try {
        Resolve and reject are called synchronously to the executor function
        executor(resolve, reject);
    } catch(e) { reject(e); }}// Add then methods to the prototype so that instance objects can use then methods
Promise.prototype.then = function (onResolved, onRejected) {
    if (this.PromiseState === 'fulfilled') {
        onResolved(this.PromiseResult);
    }
    if (this.PromiseState === 'rejected') {
        onRejected(this.PromiseResult);
    }
    if(this.PromiseState === 'pending') {this.callbacks.push({ / / 2. + + + + + + +
            onResolved,
            onRejected
        })
    }
}
Copy the code
  • This completes another small point, and running the code above yields the following result

4. The result of the then method is displayed

We all know that the object returned by calling THEN is a Promise object, so we use the built-in PROMISE to perform the following result

let p = new Promise((resolve, reject) = > {
    resolve('ok');
})
const result = p.then(value= > {
    console.log(value);
},reason= > {
    console.log(reason)
})
console.log(result)
Copy the code

The results are shown below:

With the Promise that we wrote above, we’re not going to return a Promise object, so let’s go ahead and rewrite that

1. We use a Promise inside the then method to make sure that then returns a Promise object

2. If there is no return value in then, it will return a successful Promise object. If there is a return value in then, it will return a Promise object. The onResolved callback will be executed when we are in the successful state and the onRejected callback will be called when we fail

3. If onResolved returns a Promise object in the then call, then we can run the resolve call

4. If we throw an error in a then callback, the object returned is in a failed state, using the same try catch method as above

Promise.prototype.then = function (onResolved, onRejected) {
    return new Promise((resolve, reject) = > {    / / 1. + + + + + + + +
        if (this.PromiseState === 'fulfilled') {
            try {    / / 4. + + + + + + + +
                // Get the result of executing the callback function
                let result = onResolved(this.PromiseResult);  
                / / determine
                if (result instanceof Promise) {     / / 2. + + + + + + +
                    // If it is an object of type Promise
                    result.then(v= > {
                        resolve(v);
                    }, r= >{ reject(r); })}else {   / / 3. + + + + + + +
                    // The object status of the result is successresolve(result); }}catch (e) {
                // If a throw error is detected, the state changes to failedreject(e); }}if (this.PromiseState === 'rejected') {
            onRejected(this.PromiseResult);
        }
        if (this.PromiseState === 'pending') {
            this.callbacks.push({
                onResolved,
                onRejected
            })
        }

    })
}
Copy the code

We run the following code using the Promise we wrote

// When the then callback returns a Promise
let p = new Promise((resolve, reject) = > {
    resolve('ok');
})
const res = p.then(value= > {
    return new Promise((resolve,reject) = > {
        resolve('123333'); })},reason= > {
    console.log(reason);
})
console.log(res);
// When the callback inside then returns no Promise(no return)
let p1 = new Promise((resolve, reject) = > {
    resolve('ok');
})
const res1 = p1.then(value= > {
    console.log(value)
},reason= > {
    console.log(reason);
})
console.log(res1);
// Then throws an error
let p2 = new Promise((resolve, reject) = > {
    resolve('ok');
})
const res2 = p2.then(value= > {
    throw 'error';
},reason= > {
    console.log(reason);
})
console.log(res2);
Copy the code

The running results are as follows:

5. The result of asynchronously changing the status then method is returned

Some people ask that this step has already been done in 2.2. The answer is: no

Execute the following code to find out

let p = new Promise((resolve, reject) = > {
    setTimeout(() = > {
        resolve('ok');                
    }, 1000);

})
const res = p.then(value= > {
    console.log(value)
},reason= > {
    console.log(reason);
})
console.log(res);
Copy the code

The ‘OK’ is output after a second, but the RES state is still pending and has not changed. Why? We’re doing the callback inside the.then method, and we’re going to return a state of RES based on that callback, which means that when we do the onResolved callback our state will be successful and we’re going to return a successful state to the Promise object (RES).

1. When this.promisestate === ‘pending’ is called, it doesn’t work, so we change it to a function and pass in the PromiseResult parameter

2. The next steps are the same as above to determine whether the return is a Promise object, here will not repeat the story

Promise.prototype.then = function (onResolved, onRejected) {
    const self = this;
    return new Promise((resolve, reject) = > {
        if (this.PromiseState === 'fulfilled') {
            try {
                // Get the result of executing the callback function
                let result = onResolved(this.PromiseResult);
                / / determine
                if (result instanceof Promise) {
                    // If it is an object of type Promise
                    result.then(v= > {
                        resolve(v);
                    }, r= >{ reject(r); })}else {
                    // The object status of the result is successresolve(result); }}catch(e) { reject(e); }}if (this.PromiseState === 'rejected') {
            onRejected(this.PromiseResult);
        }
        if (this.PromiseState === 'pending') {
            this.callbacks.push({
                onResolved: function () {    / / 1. + + + + + + + +
                    try {    //2. See 2.4+++++++++++ for details like the previous steps
                        // Execute the success callback
                        let result = onResolved(self.PromiseResult);
                        / / determine
                        if (result instanceof Promise) {
                            result.then(v= > {
                                resolve(v);
                            }, r= >{ reject(r); })}else{ resolve(result); }}catch(e) { reject(e); }},onRejected: function () {  / / 1. + + + + + + + +
                    try {   / / 2. + + + + + + + +
                        onRejected(self.PromiseResult);
                        let result = onRejected(self.PromiseResult);
                        / / determine
                        if (result instanceof Promise) {
                            result.then(v= > {
                                resolve(v);
                            }, r= >{ reject(r); })}else{ resolve(result); }}catch(e) { reject(e); }}})}})}Copy the code

We can then run the code in the example above to get the correct result, as shown below:

6. Then method improvement and optimization

As can be seen from the picture above, we have not improved the failed state, so we can also change the failed state. Please see 2.4 for detailed steps. Otherwise, if the Promise is called reject(), the state will still be in the pending state rather than the depressing state

if (this.PromiseState === 'rejected') {
try {
    let result = onRejected(this.PromiseResult);
    if(result instanceof Promise){
        result.then(v= > {
            resolve(v);
        },r= >{ reject(r); })}else{ resolve(result); }}catch(e) { reject(e); }}Copy the code

Have you noticed that this line of code has been used many times? We can encapsulate a function to make the code simpler and easier to maintain

// Encapsulate the function
function callback(type) {
    try {
        // Get the result of executing the callback function
        let result = type(this.PromiseResult);
        / / determine
        if (result instanceof Promise) {
            // If it is an object of type Promise
            result.then(v= > {
                resolve(v);
            }, r= >{ reject(r); })}else {
            // The object status of the result is successresolve(result); }}catch(e) { reject(e); }}Copy the code

Now, let’s just say that our then method has become pretty straightforward

Promise.prototype.then = function (onResolved, onRejected) {
    const self = this;
    return new Promise((resolve, reject) = > {
        // Encapsulate the function
        function callback(type) {
            try {
                // Get the result of executing the callback function
                let result = type(self.PromiseResult);   // Note that this refers to oh!! Remember to change
                / / determine
                if (result instanceof Promise) {
                    // If it is an object of type Promise
                    result.then(v= > {
                        resolve(v);
                    }, r= >{ reject(r); })}else {
                    // The object status of the result is successresolve(result); }}catch(e) { reject(e); }}if (this.PromiseState === 'fulfilled') {
            callback(onResolved);
        }
        if (this.PromiseState === 'rejected') {
            callback(onRejected);
        }
        if (this.PromiseState === 'pending') {
            this.callbacks.push({
                onResolved: function () {
                    callback(onResolved);
                },
                onRejected: function () { callback(onRejected); }})}})}Copy the code

!!!!! Now our then method is complete! Call a 1 in the comments

Step 3: Catch method

Then we wrap the catch method, which specifies the failed callback and returns a Promise object, using the wrapped THEN method

// Add the catch method
Promise.prototype.catch = function(onRejected){
    return this.then(undefined, onRejected);
}
Copy the code
  • Next we execute this line of code:
let p = new Promise((resolve, reject) = > {
    setTimeout(() = > {
        reject('error')},1000);

})
p.then(value= > {
    console.log(111);
}).then(value= > {
    console.log(222);
}).then(value= > {
    console.log(333);
}).catch(reason= > {
    console.log(reason);
})
Copy the code

If you know anything about promises you should know that this code is supposed to emit an error. Yes, this is the exception penetration of the catch method. If one part of it fails, the catch will immediately receive this message.

The answer is: Console. log(111) returns the Promise of a pending state. This callback will be stored on the property of p and the failed callback will be undefined: Because the second argument is not passed, reject() is executed after the state is changed, and an error is reported. The simplest reason for this failure is that the second argument was not passed

1. Of course, we can pass fewer parameters in the built-in promise. then, so we need to check whether the then callback has an onRejected parameter

2. We will also need to determine if the onResolved function is coming in. If not, the then method will not be able to be passed, so we will use the same process

Promise.prototype.then = function (onResolved, onRejected) {
    const self = this;
    // Determine the callback parameters
    if (typeofonRejected ! = ='function') {  / / 1. + + + + + + + + + + +
        onRejected = reason= > {
            throwreason; }}if (typeofonResolved ! = ='function') { / / 2. + + + + + + + + + +
        onResolved = value= > value;
        //value => {return value}
    }
    return new Promise((resolve, reject) = > {
        // Encapsulate the function
        function callback(type) {
            try {
                // Get the result of executing the callback function
                let result = type(self.PromiseResult);
                / / determine
                if (result instanceof Promise) {
                    // If it is an object of type Promise
                    result.then(v= > {
                        resolve(v);
                    }, r= >{ reject(r); })}else {
                    // The object status of the result is successresolve(result); }}catch(e) { reject(e); }}if (this.PromiseState === 'fulfilled') {
            callback(onResolved);
        }
        if (this.PromiseState === 'rejected') {
            callback(onRejected);
        }
        if (this.PromiseState === 'pending') {
            this.callbacks.push({
                onResolved: function () {
                    callback(onResolved);
                },
                onRejected: function () { callback(onRejected); }})}})}Copy the code

The result of executing the following code using the catch method we have written is:

let p = new Promise((resolve, reject) = > {
    setTimeout(() = > {
        resolve('okok');
    }, 1000);

})
p.then().then(value= > {
    console.log(222);
    throw 'error';
}).then(value= > {
    console.log(333);
}).catch(reason= > {
    console.warn(reason);
})
Copy the code

!!!!! Now our catch method is complete!! Is it easy (no, it’s a bit tricky to write it all over again)

Step 4: Wrap the resolve method

  1. Our resolve method does not belong to the method on the instance object, but to the method on the Promise function object, so we should add the following declaration, passing the value argument and returning a Promise object
  2. Again, we need to determine if the parameter passed in is a Promise object. If so, we can call the THEN method directly, and if not, we can return a Promise object to it
Promise.resolve = function(value){
    // Return the Promise object
    return new Promise((resolve,reject) = > {
       if(value instanceof Promise){
        value.then(v= > {
            resolve(v);
        },r= >{ reject(r); })}else{
           // The status is set to successresolve(value); }}); }Copy the code

Execute the following code to demonstrate:

let p = Promise.resolve('ok');
const p2 = Promise.resolve(new Promise((resolve, reject) = > {
    resolve('success');
}));
console.log(p);
console.log(p2);
Copy the code

The result is:

!!!!! This completes our resolve method!

Step 5: Reject method encapsulation

This method is similar to resolve and won’t be explained further here

// Add reject
Promise.reject = function (reason) {
    return new Promise((resolve,reject) = > {
        reject(reason);
    });
}
Copy the code

Step 6: All method encapsulation

The result of our all method is a Promise object, and the argument is usually passed in an array, and the result is determined by the array state of the Promise. If all the promises in the array succeed, the result is a successful state, and the result of the array. If one of the promises fails, The result is an object with failed promises and an array of failed promises

let p1 = new Promise((resolve, reject) = > {
    resolve('Ok');
})
let p2 = Promise.resolve('Success');
let p3 =Promise.resolve('123');
let result = Promise.all([p1,p2,p3]);
console.log(result)
Copy the code

Here are the results of the built-in Promise:

The state of the returned result is determined by the Promise in the array

How do you judge success?

1. So we are going to use traversal. To traversal the array Promises passed in, and each object can call then

2. We need to iterate through the array to determine if all of them are successful before we call resolve, rather than iterate through a promise[I], which returns the first promise if it is successful. Therefore, we need to declare a variable to count the number of successes and increment the success status by 1. After traversing the array, we make a determination that if the count value is the same as the length of the array passed in, all promises are successful. Then we call resolve to return a successful object

3. We need finish all method calls have an array to save the result, so we are here to declare an array to save as a result, if success is to save the results to the array, so we in the success callback code, we can use the push method to save, but it has a flaw, the results of the order and the order of the array may not be the same, Because the time state of the change is not necessarily, so here is a very clever place, see the following code!! (Very important!!) Arr [I] = v line, we use the elements in the array subscript to store data

4. If a promise fails, call Reject to return a failed promise

// Add the all method
Promise.all = function (promises) {
    // Return as a Promise object
    return new Promise((resolve, reject) = > {
        // Declare variables
        let count = 0;  / / 2.
        let arr = [];
        / / traverse
        for (let i = 0; i < promises.length; i++) {   / / 1.
            //
            promises[i].then(v= > {
                // The status of the object is success
                // resolve(); We can't do that. We need to iterate through the array before we call this function
                count++;  / / 2.
                // Stores the results of the current Promise object's success into an array
                //arr.push(v); This approach will be flawed!! Look at the reasons analyzed above
                arr[i] = v; // Very clever

                // Make a judgment
                if (count === promises.length) {   / / 2.resolve(arr); }},r= >{ reject(r); })}})}Copy the code

!!!!! Now our all method is wrapped!! You can see the results!

Step 7: Race encapsulation

Our race method passes in an array of parameters that are also promises, and returns a Promise object whose state is the first promise in the array to change state. Let’s see what happens when we run the code using the built-in Promise. P1 changes the state first so it’s returned with the value OK

let p1 = new Promise((resolve, reject) = > {
    resolve('Ok');
})
let p2 = Promise.resolve('Success');
let p3 = Promise.resolve('123');
let result = Promise.race([p1, p2, p3]);
console.log(result)
Copy the code

This method also needs to iterate through the array, race and all methods are different, do not need to iterate through the entire array to determine whether it is successful, who runs first will directly return the state, I don’t know what you can understand. Race encapsulation is slightly simpler

// Add the race method
Promise.race = function(promises){
    return new Promise((resolve,reject) = > {
        for(let i = 0; i < promises.length ; i++){
            promises[i].then(v= > {
                // Change the status of the returned object to success
                resolve(v);
            },r= > {
                // Change the status of the returned object to failedreject(r); })}})}Copy the code
  • Again, let’s verify that our code is correct

Execute the following code:

let p1 = new Promise((resolve, reject) = > {
    setTimeout(() = > {
        resolve('Ok');
    }, 1000);

})
let p2 = Promise.resolve('Success');
let p3 = Promise.resolve('123');
let result = Promise.race([p1, p2, p3]);
console.log(result)
Copy the code
  • Since P1 changes state after one second, promise. all should return p2. Let’s take a look at the result

Last step!! We’re done

As we all know, Promise. Then is an asynchronous execution task and a microtask, which might involve a little bit of EventLoop lore, but I won’t go into eventloop detail here. As an example, we use the built-in Promise to run the following code, The console will print 111–>333–>222 because it waits for the synchronous code to run before executing the asynchronous code. With our Promise, the console will print 111–>222–>333.

let p1 = new Promise((resolve, reject) = > {
    resolve('Ok');
    console.log('111');
})
p1.then(value= > {
    console.log('222');
})
console.log(333);
Copy the code

What do we need to do to make our THEN execution asynchronous? We make the change in the THEN method. We use a setTimeout function outside the callback to make it an asynchronous task. Just look at the new code with ++++ at the end

function Promise(executor) {
    // Add the attribute. The initial state is pending
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    // Declare attributes
    this.callbacks = [];
    // Save the instance object's this value
    const self = this;
    / / resolve function
    function resolve(data) {
        // Determine the state (state reversible)
        if(self.PromiseState ! = ='pending') return
        // Modify object state (promiseState)
        self.PromiseState = 'fulfilled';
        // Set object result value (promiseResult)
        self.PromiseResult = data;
        // Call the successful callback function
        setTimeout(() = > {      //++++++++
            self.callbacks.forEach(item= > {
                item.onResolved(data)
            })
        });

    }
    / / reject function
    function reject(data) {
        // Determine the state (state reversible)
        if(self.PromiseState ! = ='pending') return
        // Modify object state (promiseState)
        self.PromiseState = 'rejected';
        // Set object result value (promiseResult)
        self.PromiseResult = data;
        // Call the failed callback function
        setTimeout(() = > {      //++++++++
            self.callbacks.forEach(item= > {
                item.onRejected(data)
            })
        });

    }
    try {
        Resolve and reject are called synchronously to the executor function
        executor(resolve, reject);
    } catch(e) { reject(e); }}// Add then methods to the prototype so that instance objects can use then methods
Promise.prototype.then = function (onResolved, onRejected) {
    const self = this;
    // Determine the callback parameters
    if (typeofonRejected ! = ='function') {
        onRejected = reason= > {
            throwreason; }}if (typeofonResolved ! = ='function') {
        onResolved = value= > value;
        //value => {return value}
    }
    return new Promise((resolve, reject) = > {
        // Encapsulate the function
        function callback(type) {
            try {
                // Get the result of executing the callback function
                let result = type(self.PromiseResult);
                / / determine
                if (result instanceof Promise) {
                    // If it is an object of type Promise
                    result.then(v= > {
                        resolve(v);
                    }, r= >{ reject(r); })}else {
                    // The object status of the result is successresolve(result); }}catch(e) { reject(e); }}if (this.PromiseState === 'fulfilled') {
            setTimeout(() = > {     //++++++++
                callback(onResolved);
            });

        }
        if (this.PromiseState === 'rejected') {
            setTimeout(() = > {     //++++++++
                callback(onRejected);
            });
        }
        if (this.PromiseState === 'pending') {
            this.callbacks.push({
                onResolved: function () {
                    callback(onResolved);
                },
                onRejected: function () { callback(onRejected); }})}})}Copy the code

conclusion

This article is quite long, sorting also sorted for a long time, of course, the interview usually will not let you write a complete Promise, the more important is the promise. all and promise. race method these two more often test handwritten questions! Although I wrote the Promise two or three times, but the feeling is still quite complex, to consider the point quite many, so must knock more code! Code more! More code!! Important things say three times! If you like, you can click the “like” collection, I am the first time to write such a long summary article, if there is a bad place to write, please understand! Questions can be asked in the comments section! Attached behind handwritten Promise source code on Github above, there are ES5 version and ES6 with class written version

ES5 version:

function Promise(executor) {
    // Add the attribute. The initial state is pending
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    // Declare attributes
    this.callbacks = [];
    // Save the instance object's this value
    const self = this;
    / / resolve function
    function resolve(data) {
        // Determine the state (state reversible)
        if(self.PromiseState ! = ='pending') return
        // Modify object state (promiseState)
        self.PromiseState = 'fulfilled';
        // Set object result value (promiseResult)
        self.PromiseResult = data;
        // Call the successful callback function
        setTimeout(() = > {
            self.callbacks.forEach(item= > {
                item.onResolved(data)
            })
        });

    }
    / / reject function
    function reject(data) {
        // Determine the state (state reversible)
        if(self.PromiseState ! = ='pending') return
        // Modify object state (promiseState)
        self.PromiseState = 'rejected';
        // Set object result value (promiseResult)
        self.PromiseResult = data;
        // Call the failed callback function
        setTimeout(() = > {
            self.callbacks.forEach(item= > {
                item.onRejected(data)
            })
        });

    }
    try {
        Resolve and reject are called synchronously to the executor function
        executor(resolve, reject);
    } catch(e) { reject(e); }}// Add then methods to the prototype so that instance objects can use then methods
Promise.prototype.then = function (onResolved, onRejected) {
    const self = this;
    // Determine the callback parameters
    if (typeofonRejected ! = ='function') {
        onRejected = reason= > {
            throwreason; }}if (typeofonResolved ! = ='function') {
        onResolved = value= > value;
        //value => {return value}
    }
    return new Promise((resolve, reject) = > {
        // Encapsulate the function
        function callback(type) {
            try {
                // Get the result of executing the callback function
                let result = type(self.PromiseResult);
                / / determine
                if (result instanceof Promise) {
                    // If it is an object of type Promise
                    result.then(v= > {
                        resolve(v);
                    }, r= >{ reject(r); })}else {
                    // The object status of the result is successresolve(result); }}catch(e) { reject(e); }}if (this.PromiseState === 'fulfilled') {
            setTimeout(() = > {
                callback(onResolved);
            });

        }
        if (this.PromiseState === 'rejected') {
            setTimeout(() = > {
                callback(onRejected);
            });
        }
        if (this.PromiseState === 'pending') {
            this.callbacks.push({
                onResolved: function () {
                    callback(onResolved);
                },
                onRejected: function () { callback(onRejected); }})}})}// Add the catch method
Promise.prototype.catch = function (onRejected) {
    return this.then(undefined, onRejected)
}

// Add the resolve method
Promise.resolve = function (value) {
    // Return the Promise object
    return new Promise((resolve, reject) = > {
        if (value instanceof Promise) {
            value.then(v= > {
                resolve(v);
            }, r= >{ reject(r); })}else {
            // The status is set to successresolve(value); }}); }// Add reject
Promise.reject = function (reason) {
    return new Promise((resolve, reject) = > {
        reject(reason);
    });
}
// Add the all method
Promise.all = function (promises) {
    // Return as a Promise object
    return new Promise((resolve, reject) = > {
        // Declare variables
        let count = 0;
        let arr = [];
        / / traverse
        for (let i = 0; i < promises.length; i++) {
            //
            promises[i].then(v= > {
                // The status of the object is success
                // resolve(); We can't do that. We need to iterate through the array before we call this function
                count++;
                // Stores the results of the current Promise object's success into an array
                //arr.push(v); This approach will be flawed!! Look at the reasons analyzed above
                arr[i] = v; // Very clever

                // Make a judgment
                if(count === promises.length) { resolve(arr); }},r= >{ reject(r); })}})}// Add the race method
Promise.race = function (promises) {
    return new Promise((resolve, reject) = > {
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(v= > {
                // Change the status of the returned object to success
                resolve(v);
            }, r= > {
                // Change the status of the returned object to failedreject(r); })}})}Copy the code

ES6 version:

class Promise {
    // constructor
    constructor(executor) {
        // Add attributes
        this.PromiseState = 'pending';
        this.PromiseResult = null;
        // Declare attributes
        this.callbacks = [];
        // Save the value of this for the instantiated object
        const self = this;
        function resolve(data) {
            // Determine the status
            if(self.PromiseState ! = ='pending') return
            // Change the state of an object (promiseState)
            self.PromiseState = 'fulfilled';
            // Set object result value (promiseResult)
            self.PromiseResult = data;
            // Call the successful callback function
            setTimeout(() = > {
                self.callbacks.forEach(item= >{ item.onResolved(data); })}); }function reject(data) {
            // Determine the status
            if(self.PromiseState ! = ='pending') return
            // Change the state of an object (promiseState)
            self.PromiseState = 'rejected';
            // Set object result value (promiseResult)
            self.PromiseResult = data;
            // Call the failed callback function
            setTimeout(() = > {
                self.callbacks.forEach(item= >{ item.onRejected(data); })}); }try {
            // Call the executor function
            executor(resolve, reject);
        } catch (e) {
            // Change pomise status to failed
            reject(e)
        }
    }
    / / then method
    then(onResolved, onRejected) {
        const self = this;
        // Determine the callback parameters
        if (typeofonRejected ! = ='function') {
            onRejected = reason= > {
                throwreason; }}if (typeofonResolved ! = ='function') {
            onResolved = value= > value;
            //value => {return value}
        }
        return new Promise((resolve, reject) = > {
            // Encapsulate the function
            function callback(type) {
                try {
                    // Get the result of executing the callback function
                    let result = type(self.PromiseResult)
                    / / determine
                    if (result instanceof Promise) {
                        result.then(v= > {
                            resolve(v);
                        }, r= >{ reject(r); })}else {
                        // The object status of the result is success
                        resolve(result)
                    }
                } catch(e) { reject(e); }}// Call the callback function
            if (this.PromiseState === 'fulfilled') {
                setTimeout(() = > {
                    callback(onResolved);
                });
            }
            if (this.PromiseState === 'rejected') {
                setTimeout(() = > {
                    callback(onRejected);
                });
            }
            // Determine pending status
            if (this.PromiseState === 'pending') {
                // Save the callback function
                this.callbacks.push({
                    onResolved: function () {
                        callback(onResolved);
                    },
                    onRejected: function () { callback(onRejected); }})}})}/ / catch method
    catch(onRejected) {
        return this.then(undefined, onRejected);
    }
    // Add the resolve method
    static resolve(value) {
        // Return the Promise object
        return new Promise((resolve, reject) = > {
            if (value instanceof Promise) {
                value.then(v= > {
                    resolve(v);
                }, r= >{ reject(requestIdleCallback); })}else{ resolve(value); }})}// Add reject
    static reject(reason) {
        return new Promise((resolve, reject) = > {
            reject(reason)
        })
    }
    // Add the all method
    static all(promises) {
        return new Promise((resolve, reject) = > {
            // Declare variables
            let count = 0;
            let arr = []
            for (let i = 0; i < promises.length; i++) {
                promises[i].then(v= > {
                    // Succeeded in getting the object status
                    // Each promise object succeeds
                    count++;
                    // Stores the results of the current Promise object's success into the array
                    arr[i] = v;
                    / / determine
                    if(count === promises.length) { resolve(arr); }},r= > {
                    reject(r)
                })
            }
        })
    }
    // Add the race method
    static race(promises) {
        return new Promise((resolve, reject) = > {
            for (let i = 0; i < promises.length; i++) {
                promises[i].then(v= > {
                    // Change the status of the returned object to success
                    resolve(v);
                }, r= > {
                    reject(r)
                })
            }
        })
    }
}

Copy the code

The above code has been uploaded to Github, you can click here to see the source code! Like the words of the collection oh! That’s it for now.