Write a Promise

Preliminary knowledge

  1. Function objects and instance objects

Function object: When a function is used as an object, it is called a function object for short

  1. Two types of callback functions

Synchronous callback functions: understand: execute immediately, complete the execution, not put in the callback queue example: array traversal associated with the callback /Promise executor function

const arr = [1.2 ,3];
arr.forEach(item= > {
    console.log(item)
});
console.log('the forEach ()');
Copy the code

Asynchronous callback to understand: not executed immediately, can be carried in the future in the callback queue example: timer callback/ajax callback/Promise success | failure callback

setTimeout(() = > {
    console.log('timeout callback()');
}, 0);
console.log('the setTimeout ()');
Copy the code
  1. JS error handling 3.1 error types

    • ReferenceError: The referenced variable does not exist
    • TypeError: The data type is incorrect
    • RangeError: The data value is not in its allowed range
    • SyntaxError: SyntaxError

    3.2 Error Handling Error: try… Catch: throw error 3.3 Error type Error message error stack

    try {
        const obj;
        console.log(obj.xxx);
    } catch(error) {
        /* Error: - message - stack */
        console.log(error.message);
        console.log(error.stack);
    }
    console.log('After an error, the code continues.')
    
    function maybeError() {
        if (Math.random() < 0.5) {
            console.log('right');
        } else {
            throw new Error('something wrong! '); }}try {
        maybeError();
    } catch(e) {
        console.log(e.message);
        console.log(e.stack);
    }
    
    Copy the code

Promise to use

What is a Promise?

  1. Abstract expression

Promise is a new solution for asynchronous programming in JS.

  1. The specific expression of
  • Syntactically: Promise is a constructor
  • Functionally: A Promise object is used to encapsulate an asynchronous operation and retrieve its results

Promise’s status changed

  1. Pending a fulfilled
  2. Pending a rejected

Note: A Promise object can only change its state once, with a result data whether it is a success or an event. The data of the successful result is generally called value, and the data of the failure result is generally called Reason.

Which comes first, changing the promise state or specifying the callback function?

  1. It is normal to specify the callback and then change the state, but it is also possible to change the state and then specify the callback
  2. How do I change the state before specifying a callback?
    • Call resolve()/reject() directly in the executor
    • Call then() after a longer delay
  3. When will the data be available?
    • If the callback is executed first, the callback function is called when the state changes and the data is obtained
    • If the state is changed first, when the callback is specified, the callback function is called and the data is obtained
  4. Promise. Then () returnsThe new promise objectWhat determines the resulting state of?
    • Simple: is determined by the result of the execution of the callback function specified by then()
    • Detailed expression:
      • If an exception is thrown, the promise will be rejected and reason will be the exception thrown
      • If any value that is not a promise is returned, the new promise becomes fulfilled and the value is the returned value
      • If A Promise object A is returned, the result of A is treated as the result of the new Promise object

Write a Promise

1. IIFE

module.exports = Promise

const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

function Promise(executor) {
    this.status = PENDING;
    this.data = undefined;
    this.callbacks = [];

    const resolve = (value) = > {
        if (this.status ! == PENDING) {return;
        }
        this.status = FULFILLED;
        this.data = value;
        if (this.callbacks.length > 0) {
            queueMicrotask(() = > {
                this.callbacks.forEach(element= >{ element.onResolved() }); }}})const reject = (value) = > {
        if (this.status ! == PENDING) {return;
        }
        this.status = REJECTED;
        this.data = value;
        if (this.callbacks.length > 0) {
            queueMicrotask(() = > {
                this.callbacks.forEach(element= > {
                    element.onRejected()
                })
            })
        }
    }

    try {
        executor(resolve, reject);
    } catch(err) { reject(err); }}All methods, whether constructors or instance objects, return a Promise object
Promise.prototype.then = function (onResolved, onRejected) {
    if (typeofonResolved ! = ="function") {
        onResolved = value= > value;
    }

    if (typeofonRejected ! = ="function") {
        onRejected = reason= > { throw reason };
    }
    return new Promise((resolve, reject) = > {
        const executeResult = (cb) = > {
            try {
                let p = cb(this.data);
                if (p instanceof Promise) {
                    p.then(resolve, reject);
                } else{ resolve(p); }}catch(err) { reject(err); }}const handlePending = () = > {
            this.callbacks.push({
                onResolved: () = > {
                    executeResult(onResolved)
                },
                onRejected: () = > {
                    executeResult(onRejected)
                }
            })
        }
        const handleFulFilled = () = > {
            queueMicrotask(() = > {
                executeResult(onResolved)
            })
        }
        const handleRejected = () = > {
            queueMicrotask(() = > {
                executeResult(onRejected)
            })
        }
        // Each of the promise instances is treated differently based on the three states
        switch (this.status) {
            case PENDING:
                handlePending();
                break;
            case FULFILLED:
                handleFulFilled();
                break;
            case REJECTED:
                handleRejected();
                break;
            default:
                throw new Error("promise status is" + this.status)
        }
    })
}


Promise.prototype.catch = function (onRejected) {
    return this.then(undefined, onRejected);
}

Promise.resolve = function (value) {
    return new Promise((resolve, reject) = > {
        if (value instanceof Promise) {
            value.then(resolve, reject);
        } else{ resolve(value); }})}Promise.reject = function (value) {
    return new Promise((resolve, reject) = >{ reject(value); })}Promise.race = function (promises) {
    return new Promise((resolve, reject) = > {
        promises.forEach(p= > {
            p.then(
                value= > {
                    resolve(value)
                },
                reason= > {
                    reject(reason)
                }
            )
        })
    })
}

Promise.all = function (promises) {
    return new Promise((resolve, reject) = > {
        let count = 0;
        let result = new Array(promise.length);

        promises.forEach((p, index) = > {
            p.then(
                value= > {
                    count++;
                    result[index] = value;

                    if(count === promises.length) { resolve(result); }},reason= > {
                    reject([reason])
                }
            )
        })
    })
}


Copy the code

2. ES6 – class version



const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

class Promise {
    constructor(executor) {
        this.status = PENDING;
        this.data = undefined;
        this.callbacks = [];

        const resolve = (value) = > {
            if (this.status ! == PENDING) {return;
            }

            this.status = FULFILLED;
            this.data = value;

            if (this.callbacks.length > 0) {
                queueMicrotask(() = > {
                    this.callbacks.forEach(cb= > {
                        cb.onResolved(value)
                    })
                })
            }
        }
        const reject = (value) = > {
            if (this.status ! == PENDING) {return;
            }
            this.status = REJECTED;
            this.data = value;

            if (this.callbacks.length > 0) {
                queueMicrotask(() = > {
                    this.callbacks.forEach(cb= > {
                        cb.onRejected(value)
                    })
                })
            }
        }

        try {
            executor(resolve, reject);
        } catch(error) { reject(error); }}then(onResolved, onRejected) {
        return new Promise((resolve, reject) = > {
            const handleResult = (handler) = > {
                try {
                    let p = handler(this.data);
                    if (p instanceof Promise) {
                        p.then(resolve, reject);
                    } else{ resolve(p); }}catch(error) { reject(error); }}const handlePending = () = > {
                this.callbacks.push({
                    onResolved: () = > {
                        handleResult(onResolved);
                    },
                    onRejected: () = >{ handleResult(onRejected); }})}switch (this.status) {
                case PENDING:
                    handlePending();
                    break;
                case FULFILLED:
                    queueMicrotask(() = > {
                        handleResult(onResolved);
                    })
                    break;
                case REJECTED:
                    queueMicrotask(() = > {
                        handleResult(onRejected);
                    })
                    break;
                default:
                    throw new Error("status is wrong" + this.status); }})}catch(onRejected) {
        return this.then(null, onRejected);
    }

    static resolved(value) {
        return new Promise((resolve, reject) = > {
            if (value instanceof Promise) {
                value.then(resolve, reject);
            } else{ resolve(value); }})}static rejected(value) {
        return new Promise((resolve, reject) = > {
            reject(value)
        })
    }
    static race(promises) {
        return new Promise((resolve, reject) = > {
            promises.forEach(p= > {
                p.then(
                    value= > {
                        resolve(value);
                    },
                    reason= >{ reject(reason); })})}static all(promises) {
        return new Promise((resolve, reject) = > {
            let count = 0;
            let result = new Array(promises.length);
            promises.forEach((p, index) = > {
                p.then(
                    value= > {
                        count++;
                        result[index] = value;
                        if(count === promises.length) { resolve(result); }},reason= > {
                        reject([reason])
                    }
                )
            })
        })
    }
}
module.exports = Promise
Copy the code

3. The TS

Async and await

Syntax: [return_value] = await expression

  1. Async function

The return value of the function is a Promise object and the result of the Promise object is determined by the return value of the async function execution

  1. Await the expression

The expression to the right of the await is generally a Promise object, but can also be any other value. If the expression is a Promise object, the await returns the value of the promise’s success. If the expression is some other value, use this value directly as the return value of the await

  1. Note:

“Await” must be written in async functions, but async functions can have no “await”. If the promise of “await” fails, an exception will be thrown. Catch to catch processing

Macro and micro queues (browser)

int main(a) {

    / /...
    
    //event loop
    while(true) {
        / / the queue
        
        / / macro queue}}Copy the code
  • Macro queue
    • The timer
  • The queue
    • Observer API
    • The callback registered in the then method

The JS engine executes the code:

  1. Synchronization code
  2. The microtask queue needs to be emptied before the first macro task is fetched and executed

The interview questions

setTimeout(() = > {
    console.log("0");
}, 0);

new Promise((resolve, reject) = > {
    console.log("1");
    resolve()
}).then(() = > {
    console.log("2");
    new Promise((resolve, reject) = > {
        console.log("3");
        resolve();
    }).then(() = > {
        console.log("4");
    }).then(() = > {
        console.log("5");
    })
}).then(() = > {
    console.log("6");
})

new Promise((resolve, reject) = > {
    console.log("Seven")
    resolve()
}).then(() = > {
    console.log("8")})Copy the code