Handwritten Promise ideas

First step by step analysis, step by step to write code, and finally have a complete example

1. Promise is a class whose argument is a function executor that executes immediately. The function executor takes two arguments, a success callback resolve and a failure callback reject

class MyPromise {
    constructor(executor) {
        executor(this.resolve.bind(this), this.reject.bind(this));
    }
    resolve(value) {}
    reject(reason){}}Copy the code

Three pending in the state, 2. Promise fulfilled, rejected, and the initial state is pending, when the promise of change once the state is unable to change

// Define constant values externally
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
const PENDING = 'pending'; .constructor(executor) {
    // Sets the current promise state value
    this.status = PENDING;
    executor(this.resolve.bind(this), this.reject.bind(this)); }...Copy the code

3. The resolve/reject method changes the state of the current PROMISE

.resolve(value) {
    // If the current state is not the initial state, it cannot be modified
    if (this.status ! == PENDING)return;
    // Change the current status to succeeded
    this.status = FULFILLED; }...reject(reason) {
    // If the current state is not the initial state, it cannot be modified
    if (this.status ! == PENDING)return;
    // Change the current status to failed
    this.status = REJECTED;
}
Copy the code

4. Promise has a then method that takes two arguments, the first being the promise’s successful callback and the second a failed callback

/* Because successCallback and failCallback accept arguments in resolve and Reject, success and failure values */ are logged.constructor(executor){...// Records the value returned on success
    this.value = undefined;
    // Records the value returned on failure
    this.reason = undefined; . }...resolve(value){...// Records the value returned on success
    this.value = value; }...reject(reason){...// Records the value returned on failure
    this.reason = reason; }...then(successCallback, failCallback) {
     if (this.status === FULFILLED) {
         successCallback(this.value)
     } else if (this.status === REJECTED) {
         failCallback(this.reason)
     }
}
......
Copy the code

5. When the function executor is synchronous, the state of the promise changes immediately, so then can receive the state processing success and failure, but when the function executor is asynchronous, the state of the promise is waiting state, and the success and failure callback needs to be recorded. Call the corresponding callback function after the asynchronous executor completes execution

/* So we need two variables to store success and failure callbacks for the then method. When the asynchronous executor completes, resolve/reject executes success and failure callbacks for the then method, respectively.constructor(executor){...// Record a successful callback
    this.successCallback = undefined;
    // Record the failed callback
    this.failCallback = undefined; . }...then(successCallback, failCallback){...else if (this.status === PENDING) {
        this.successCallback = successCallback;
        this.failCallback = failCallback; }}...resolve(value){...// If the actuator is asynchronous, the execution of the actuator is complete, and the callback must be executed successfully
    this.successCallback(this.value); }...reject(reason){...// If the actuator is asynchronous, the actuator completes and a failure callback needs to be executed
    this.failCallback(this.reason); }...Copy the code

6. A promise can also perform multiple then method, and the callback will perform, then method when the function actuators for synchronous, we don’t need to do to deal with, but when the function of actuators is asynchronous, we need to keep every callback function then method, when the function of the actuator has been completed, So, storing success and failure callbacks is an array

// The variables storing successful and failed callbacks are arrays.constructor(executor){...// Record a successful callback
    this.successCallback = [];
    // Record the failed callback
    this.failCallback = []; . }...then(successCallback, failCallback){...else if (this.status === PENDING) {
        this.successCallback.push(successCallback);
        this.failCallback.push(failCallback); }}...resolve(value){...// If the actuator is asynchronous, the execution of the actuator is complete, and the callback must be executed successfully
    while (this.successCallback.length)this.successCallback.shift()(this.value); }...reject(reason){...// If the actuator is asynchronous, the actuator completes and a failure callback needs to be executed
    while (this.failCallback.length) this.failCallback.shift()(this.reason); }...Copy the code

7. A promise can be chained to a then method, meaning that the then method must return a promise. The second THEN is a call to the promise object returned in the first THEN, so that the API can continuously pass parameters without going back to hell. If a normal value is returned in a successful callback in the first THEN, the next THEN will receive the value, or if it is a PROMISE, the next THEN will wait for the promise to complete and then pass the value on

then(successCallback, failCallback) {
     / * to determine whether the return value of a current successCallback/failCallback asynchronous, if asynchronous need to wait until the asynchronous completion * /
     const resolvePromise = (x, resolve, reject) = > {
        if (x instanceof MyPromise) {
            x.then(resolve, reject);
        } else{ resolve(x); }}const promise2 = new MyPromise((resolve, reject) = > {
         if (this.status === FULFILLED) {
             const x = successCallback(this.value)
             resolvePromise(x, resolve, reject);
         } else if (this.status === REJECTED) {
             const x = failCallback(this.reason);
             resolvePromise(x, resolve, reject)
         } else if (this.status === PENDING) {
            this.successCallback.push(() = > {
                const x = successCallback(this.value);
                resolvePromise(promise2, x, resolve, reject)
            });
            this.failCallback.push(() = > {
                const x = failCallback(this.reason) resolvePromise(promise2, x, resolve, reject) }); }})returnpromise2; }...// There is no need to pass this.value/this.reason
resolve(value){...// If the actuator is asynchronous, the execution of the actuator is complete, and the callback must be executed successfully
    while (this.successCallback.length) this.successCallback.shift()(); }...reject(reason){...// If the actuator is asynchronous, the actuator completes and a failure callback needs to be executed
    while (this.failCallback.length) this.failCallback.shift()(); }...Copy the code

8. You cannot return your promose object in the then method, otherwise a circular call will occur

/* If x is the same as promise2, use a timer to change the callback to an asynchronous */
then(successCallback, failCallback) {
     / * to determine whether the return value of a current successCallback/failCallback asynchronous, if asynchronous You may need to wait until the asynchronous completion * /
     const resolvePromise = (promise, x, resolve, reject) = > {
        if (promise === x) {
            return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}if (x instanceof MyPromise) {
            x.then(resolve, reject);
        } else{ resolve(x); }}const promise2 = new MyPromise((resolve, reject) = > {
         if (this.status === FULFILLED) {
             setTimeout(() = > {
                 const x = successCallback(this.value)
                 resolvePromise(promise2, x, resolve, reject);             
             }, 0)}else if (this.status === REJECTED) {
             setTimeout(() = > {
                 const x = failCallback(this.reason);
                 resolvePromise(promise2, x, resolve, reject)
             }, 0)}else if (this.status === PENDING) {
            this.successCallback.push(() = > {
                setTimeout(() = > {
                    const x = successCallback(this.value);
                    resolvePromise(promise2, x, resolve, reject)
                }, 0)});this.failCallback.push(() = > {
                setTimeout(() = > {
                    const x = failCallback(this.reason)
                    resolvePromise(promise2, x, resolve, reject)
                }, 0)}); }})return promise2;
}
Copy the code

9. Error handling: Reject callback is executed when the function executor fails, reject callback is executed when the THEN method fails, reject callback is executed when the THEN method fails, reject callback is executed when the THEN method fails, reject callback is executed when the THEN method fails, reject callback is executed when the THEN method fails, reject callback is executed

// First grab the actuator error
constructor(executor){...The executor, which executes immediately, takes two arguments, a successful callback and a failed callback
    try {
        executor(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
        this.reject(error)
    }
}

// Grab errors in then
then(successCallback, failCallback) {
     / * to determine whether the return value of a current successCallback/failCallback asynchronous, if asynchronous You may need to wait until the asynchronous completion * /
     const resolvePromise = (promise, x, resolve, reject) = > {
        if (promise === x) {
            return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}if (x instanceof MyPromise) {
            x.then(resolve, reject);
        } else{ resolve(x); }}const promise2 = new MyPromise((resolve, reject) = > {
         if (this.status === FULFILLED) {
             setTimeout(() = > {
                 try {
                     const x = successCallback(this.value)
                     resolvePromise(promise2, x, resolve, reject);                    
                 } catch (error) {
                     reject(error)
                 }
             }, 0)}else if (this.status === REJECTED) {
             setTimeout(() = > {
                 try {
                     const x = failCallback(this.reason);
                     resolvePromise(promise2, x, resolve, reject)                
                 } catch (error) {
                     reject(error)
                 }
             }, 0)}else if (this.status === PENDING) {
            this.successCallback.push(() = > {
                setTimeout(() = > {
                     try {
                        const x = successCallback(this.value);
                        resolvePromise(promise2, x, resolve, reject)            
                     } catch (error) {
                         reject(error)
                     }
                }, 0)});this.failCallback.push(() = > {
                setTimeout(() = > {
                     try {
                        const x = failCallback(this.reason)
                        resolvePromise(promise2, x, resolve, reject)        
                     } catch (error) {
                         reject(error)
                     }
                }, 0)}); }})return promise2;
}
Copy the code

Resolve (resolve, resolve); resolve (resolve, resolve); resolve (resolve, resolve)

then(successCallback, failCallback) {
    // Arguments become optional, passing similar functions when no arguments are passed
    // If it is a normal value, ignore it
    if (successCallback) {
        if (typeofsuccessCallback ! = ='function') {
            successCallback = value= >value; }}else {
        successCallback = value= > value;
    }
    if (failCallback) {
        if (typeoffailCallback ! = ='function') {
            reason= > { throw reason }
        }
    } else {
        reason= > { throw reason }
    }
    ......
}
Copy the code

Promise. All is a static method that can execute multiple promises, as well as normal values

static all(array) {
    const result = [];
    let i = 0;
    return new MyPromise((resolve, reject) = > {
        const addData = (key, value) = > {
            result[key] = value;
            i++;
            if (i === array.length) {
                resolve(result);
            }
        }
        array.forEach((item, index) = > {
            if (item instanceof MyPromise) {
                item.then(value= > addData(index, value), error= > reject(error))
            } else{ addData(index, item); }})})}Copy the code

Resolve is a static method that passes a single argument, and returns a promise if it is a normal argument, or a promise if it is a promise

static resolve(item) {
    if (item instanceof MyPromise) {
        return item;
    } else {
        return new MyPromise(resolve= > {
            resolve(item)
        })
    }
}
Copy the code

The promise.reject method passes an argument, returns a promise, and passes the argument down into the second callback of the then method

static reject(error) {
    return new MyPromise((resolve, reject) = > {
        reject(error)
    })
    
}
Copy the code

If a finally method returns a promise, then must wait. If a finally method returns a promise, then must wait. If a finally method returns a promise, then must wait

finally(callback) {
    return this.then(value= > {
        const x = callback();
        return MyPromise.resolve(x).then(() = > value)
    }, error= > {
        const x = callback();
        return MyPromise.reject(x).then(undefined.() = > error)
    })
}
Copy the code

15. The catch method is equivalent to executing a failed callback in then

catch(callback) {
    this.then(undefined, callback);
}
Copy the code

Promise complete example

const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
const PENDING = 'pending';

/* Bind this declaratively */; /* bind this declaratively */

class MyPromise {
    constructor(executor) {
        // Sets the current promise state value
        this.status = PENDING;
        // Records the value returned on success
        this.value = undefined;
        // Records the value returned on failure
        this.reason = undefined;
        // Record a successful callback
        this.successCallback = [];
        // Record the failed callback
        this.failCallback = [];

        The executor, which executes immediately, takes two arguments, a successful callback and a failed callback
        try {
            executor(this.resolve.bind(this), this.reject.bind(this));
        } catch (error) {
            this.reject(error)
        }
    }

    resolve(value) {
        // If the current state is not the initial state, it cannot be modified
        if (this.status ! == PENDING)return;
        // Change the current status to succeeded
        this.status = FULFILLED;
        // Records the value returned on success
        this.value = value;
        // If the actuator is asynchronous, the execution of the actuator is complete, and the callback must be executed successfully
        while (this.successCallback.length) this.successCallback.shift()();
    }

    reject(reason) {
        // If the current state is not the initial state, it cannot be modified
        if (this.status ! == PENDING)return;
        // Change the current status to failed
        this.status = REJECTED;
        // Records the value returned on failure
        this.reason = reason;
        // If the actuator is asynchronous, the actuator completes and a failure callback needs to be executed
        while (this.failCallback.length) this.failCallback.shift()();
    }

    then(successCallback, failCallback) {
        // Arguments become optional, passing similar functions when no arguments are passed
        // If it is a normal value, ignore it
        if (successCallback) {
            if (typeofsuccessCallback ! = ='function') {
                successCallback = value= >value; }}else {
            successCallback = value= > value;
        }
        if (failCallback) {
            if (typeoffailCallback ! = ='function') {
                reason= > { throw reason }
            }
        } else {
            reason= > { throw reason }
        }
        const resolvePromise = (promise2, x, resolve, reject) = > {
            if (promise2 === x) {
                return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}if (x instanceof MyPromise) {
                x.then(resolve, reject);
            } else{ resolve(x); }}const promise2 = new MyPromise((resolve, reject) = > {
            if (this.status === FULFILLED) {
                setTimeout(() = > {
                    try {
                        const x = successCallback(this.value);
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0)}else if (this.status === REJECTED) {
                setTimeout(() = > {
                    try {
                        const x = failCallback(this.reason);
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0)}else if (this.status === PENDING) {
                // When the executor is asynchronous, the callback function needs to be recorded and executed after the executor completes
                this.successCallback.push(() = > {
                    setTimeout(() = > {
                        try {
                            const x = successCallback(this.value);
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)});this.failCallback.push(() = > {
                    setTimeout(() = > {
                        try {
                            const x = failCallback(this.reason)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)}); }})return promise2;
    }

    finally(callback) {
        return this.then(value= > {
            const x = callback();
            return MyPromise.resolve(x).then(() = > value)
        }, error= > {
            const x = callback();
            return MyPromise.reject(x).then(undefined.() = > error)
        })
    }

    catch(callback) {
        this.then(undefined, callback);
    }

    static all(array) {
        const result = [];
        let i = 0;
        return new MyPromise((resolve, reject) = > {
            const addData = (key, value) = > {
                result[key] = value;
                i++;
                if (i === array.length) {
                    resolve(result);
                }
            }
            array.forEach((item, index) = > {
                if (item instanceof MyPromise) {
                    item.then(value= > addData(index, value), error= > reject(error))
                } else{ addData(index, item); }})})}static resolve(item) {
        if (item instanceof MyPromise) {
            return item;
        } else {
            return new MyPromise(resolve= > {
                resolve(item)
            })
        }
    }

    static reject(error) {
        return new MyPromise((resolve, reject) = > {
            reject(error)
        })
    }
}
Copy the code