Personal feeling

After a week of business destruction, even after learning the pull check course, there is no way to start, while Saturday finally can complete the handwritten Promise wish, all the learning content is in this code annotation. The first big paragraph is the overall development process and then comments the corresponding code in the code. Finally, I have mastered promise. I still feel that this course has solved the pain point about promise in the interview well, and I don’t need to worry about the interview questions in this aspect in the future.

Build up your knowledge of asynchronous programming before you start coding.

Asynchronous programming

Javascript is single-threaded to prevent misoperations on the DOM. But browsers are multithreaded, including GUI threads, JS engine threads, timed trigger threads, event-triggered threads, and asynchronous HTTP request threads. To ensure accurate UI rendering, the UI rendering thread and the JavaScript engine are mutually exclusive.

The asynchronous mode does not wait for the end of the task to start the next task, but starts the next task immediately after it is started. The subsequent logic of a time-consuming function is defined in the form of a callback function.

Asynchronous mode is very important to the JavaScript language because without it you cannot handle a large number of time-consuming tasks simultaneously. For developers. The biggest difficulty with asynchrony under a single thread is the out-of-order execution of the code.

Event loop

1. When javascript encounters a synchronization task, it puts it on the execution stack (first in, last out) 2. 3. After the asynchronous task is complete, other threads push the asynchronous callback event 4 to the task queue. After all tasks in the execution stack are executed, the callback events in the task queue enter the execution stack in sequence and execute 5. Repeat the above four steps

Asynchronous tasks are divided into macro tasks and micro tasks. After each execution stack is cleared, the callback event of the micro task queue is first pushed to the task stack (the micro task regenerated by the micro task is also executed in this cycle), and then a macro task is pushed after the task stack is cleared. Microtask: Promise MutationObserver queueMicroTask Macro task: setTimeout setInterval script

Write a Promise

//Promise is a class
//Promise has three types of pending, depressing, and Rejected
// Promise has two base methods: resolve and reject
// Resolve and reject change the Promise state
// Resolve will be postponed from pending-> pity, reject will be postponed from pending-> Rejected, once the state is changed, it cannot be changed again
The Promise constructor accepts an executor that takes resolve, reject, and the actual method of the Promise class
//Promise has a then method that accepts two callbacks, one successful and one failed, depending on the state to determine which callback to execute. The callback argument is optional.
Resolve and reject pass arguments to the then callback, so declare a value and a reason in the class to hold arguments
// Then returns a Promise(the key point of the chain call), and deferred execution. The state of the Promise changes during the then execution
// Compatible promises contain delayed cases that cache success and failure functions. Since a Promise can have multiple "THEN" s, use array caching
Resolve and reject empty the cache callback array. Resolve or reject empty the cache callback array with Shift
// The state of the Promise returned by then is determined by the result of the callback function. If the callback function returns a normal object, then returns a resolve state. If a Promise object is returned, the state and value of the Promise object are passed to the Promise returned by then, and the Promise object cannot be itself
// In order to implement the previous step, we need to write a method resolvePromise
// All actuator steps require error handling
// Then this step ends

// promise.all () is a static method that can be called directly from a class, and is declared with the static keyword
// promise.all () receives an array (either a normal object or a Promise object)
// when all the Promise objects in the array are fulfilled, return a fullfilled Promise with the values returned by the Promise. If there is a single Promise in the array, return a fullfilled Promise object (rejected)
// In promise. all, the Promise object in the parameter array is treated separately from the normal object, if promise. resolve can be used otherwise
//Promise.all returns a Promise object that executes through the array of parameters in its executor, putting the result into the array, and requiring a counter to determine if it's done

// The race method is similar

Resolve returns the parameter if it is a Promise object, or one if it is not a Promise object

// Finally takes a function argument that will be executed regardless of the state of the Promise object and returns the current Promise value. And when the callback returns a Promise, it waits until the return Promise is completed before continuing with the subsequent THEN

// The catch method, which takes a function argument and returns a Promise. The Promise callback returns a decision, also using the then method


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

//Promise is a class
class LrbPromise {
    status = PENDING;  //Promise has three types of pending, depressing, and Rejected
    Resolve and reject pass arguments to the then callback, so declare a value and a reason in the class to hold arguments
    value = void 0;
    reason = void 0;
    successCallbackList = [];
    failCallbackList = [];
    constructor(executor) { The Promise constructor accepts an executor that takes resolve, reject, and the actual method of the Promise class
        try { // All actuator steps require error handling
            executor(this.resolve, this.reject);
        } catch (e) {
            this.reject(e);
        }
    }
    resolve = (value) = > {  // Note the use of the pointy function to preserve this
        if (this.status ! == PENDING)return; // Once the state is changed, it cannot be changed again
        this.status = FULFILLED; // Resolve will change the Promise state from pending-> depressing
        this.value = value; Resolve and reject pass arguments to the then callback, so declare a value and a reason in the class to hold arguments
        while(this.successCallbackList.length) { Resolve and reject empty the cache callback array. Resolve or reject empty the cache callback array with Shift
            this.successCallbackList.shift()()
        }
    }
    reject = (reason) = > { // Note the use of the pointy function to preserve this
        if (this.status ! == PENDING)return; // Once the state is changed, it cannot be changed again
        this.status = REJECTED; // Reject changes the Promise state from Pending -> Rejected
        this.reason = reason; Resolve and reject pass arguments to the then callback, so declare a value and a reason in the class to hold arguments
        while(this.failCallbackList.length) { Resolve and reject empty the cache callback array. Resolve or reject empty the cache callback array with Shift
            this.failCallbackList.shift()()
        }
    }
    //Promise has a then method that accepts two callbacks, one successful and one failed, depending on the state to determine which callback to execute. The callback argument is optional.
    then (successCallback, failCallback) {
        successCallback = successCallback ? successCallback : value= > value; // Callback arguments are optional (i.e., passable or not)
        failCallback = failCallback ? failCallback : e= > { throw(e) }
        const promise2 = new LrbPromise((resolve, reject) = > { // Note that the tip function preserves this, and the state of the Promise returned by then is determined by the result of the callback
            if (this.status === FULFILLED) { // If this is a big state, the callback will be implemented successfully
                setTimeout(() = > { // Note the use of the pointy function to preserve this
                    try { // All actuator steps require error handling
                        let x = successCallback(this.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e)
                    }
                });
            } else if (this.status === REJECTED) { // The callback is performed successfully in the Rejected state
                setTimeout(() = > {
                    try { // All actuator steps require error handling
                        let x = failCallback(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e)
                    }
                });
            } else {
                // Compatible promises contain delayed cases that cache success and failure functions. Since a Promise can have multiple "THEN" s, use array caching
                this.successCallbackList.push(() = > {
                    setTimeout(() = > { // Note the use of the pointy function to preserve this
                        try { // All actuator steps require error handling
                            let x = successCallback(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)});this.failCallbackList.push(() = > {
                    setTimeout(() = > { // Note the use of the pointy function to preserve this
                        try { // All actuator steps require error handling
                            let x = failCallback(this.reason);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)});// this.failCallbackList.push( () => {
                // setTimeout(() => {
                // try {
                // let x = failCallback(this.reason);
                // resolvePromise (promise2, x, resolve, reject);
                // } catch (e) {
                // reject(e)
                / /}
                / /}, 0)
                // });}})return promise2;
    }
    Resolve returns the parameter if it is a Promise object, or one if it is not a Promise object
    static resolve (value) {
        return value instanceof LrbPromise ? value : new LrbPromise(resolve= > resolve(x));
    }
    //Promise.all() is a static method that accepts an array of arguments because it can be called directly from a class
    //Promise.all returns a Promise object that executes through the array of parameters in its executor, putting the result into the array, and requiring a counter to determine if it's done
    static all (arr) {
        return new LrbPromise((resolve, reject) = > {
            if (!Array.isArray(arr))  return reject(new TypeError('Please pass in an array'));
            const result = [];
            let counter = 0;
            // when all the Promise objects in the array are fulfilled, return a fullfilled Promise with the values returned by the Promise. If there is a single Promise in the array, return a fullfilled Promise object (rejected)
            function addValue (i, val) {
                result[i] = val;
                counter++;
                if (counter === arr.length) {
                    resolve(result)
                }
            }
            for (let i = 0; i < arr.length; i++) {
                // In promise. all, the Promise object in the parameter array is treated separately from the normal object, if promise. resolve can be used otherwise
                if (arr[i] instanceof LrbPromise) {
                    arr[i].then(res= > {
                        addValue(i, res);
                    }, reject)
                } else{ addValue(i, arr[i]); }}// If there is promise.resolve
            // for (let i = 0; i < arr.length; i++) {
            // LrbPromise.resolve(arr[i]).then(res => {
            // result[i] = res;
            // counter++;
            // if (counter === arr.length) {
            // resolve(result)
            / /}
            // }, reject)
            // }})}// Finally takes a function argument that will be executed regardless of the state of the Promise object and returns the current Promise value. And when the callback returns a Promise, it waits until the return Promise is completed before continuing with the subsequent THEN
    finally (callback) {
        return this.then((value) = > {
            return LrbPromise.resolve(callback()).then(() = > value)
        }, (reason) = > {
            return LrbPromise.resolve(callback()).then(() = > { throw reason })
        })
    }
    // The catch method, which takes a function argument and returns a Promise. The Promise callback returns a decision, also using the then method
    catch (callback) {
        return this.then(void 0, callback)
    }
}

// The state of the Promise returned by then is determined by the result of the callback function. If the callback function returns a normal object, then returns a resolve state. If a Promise object is returned, the state and value of the Promise object are passed to the Promise returned by then, and the Promise object cannot be itself
function resolvePromise (promise, x, resolve, reject) {
    if (promise === x) {
        reject(new TypeError('Can't return yourself')); // You can try throwing directly here
    }
    if (x instanceof LrbPromise) {
        x.then(resolve, reject);
    } else{ resolve(x); }}function getPromise () {
    return new LrbPromise((resolve, reject) = > {
        // resolve(1)
        // setTimeout(() => {
        // resolve(' succeed ')
        // });
            // resolve(' succeed ')
        reject('failure')})}const p2 = getPromise();

p2.then()
    .then(res= > console.log('success:', res), reason= > console.log('failure:', reason))
// const p1 = new LrbPromise((resolve, reject) => {
// // resolve(1)
// // reject(1)
// }).then((value) => {
// console.log(value);
// }, (reason) => {
// console.log(reason);
// })
Copy the code