1. The term

  • 1.promiseIs an object or function that has then methods
  • 2.thenableIs an object or function that has then methods
  • 3.valueIs the value of the promise success state, and the value is of any type that conforms to the JS specification
  • 4.exceptionIs the value thrown by a throw exception
  • 5.reasonIs the value of the promise failed state

2. Promise A + specification

2.1. Promise States

A promise must be in one of three states:

  • 1. Pending
  • This is a big pity. ∏ is my dream
  • 3. Rejected

2.1.1. Pending

  • The initialization state can be changed
  • It can go to a success state or a failure state

2.1.2. Fulfilled

  • The final state is immutable
  • pending->resolve(value)->fulfilled
  • You must have a value

2.1.3. Rejected

  • The final state is immutable
  • pending->reject(reason)->rejected
  • You must have a reason value

2.1.4. Change of state

This is a big pity. “pending->reject-> Rejected” is a big pity. “Pending ->throw new Error(”)-> Failed” pending->throw new Error(”)-> Rejected

2.2. Then the method

A Promise must provide a THEN method that accesses the final result, either value or Reason

promise.then(onFulfilled,onRejected);
Copy the code

2.2.1. Parameter Requirements

  • ifonFulfilledIs not a function and must ignore value penetration.
  • ifonRejectedIs not a function and must ignore value penetration.

2.2.2. OnFulfilled

  • When the state becomes depressing, ondepressing should be called with the parameter value
  • OnFulfilled is not called until the state is fulfilled
  • Ondepressing cannot be called many times

2.2.3. OnRejected

  • When the status is Rejected, use onRejected and set the parameter to reason
  • OnRejected is not called until the state is Rejected
  • OnRejected cannot be called multiple times

2.2.4. OnFulfilled and onRejected should be performed asynchronously

This can be done through macro tasks or microtasks

  • Macro task: setTimeout or setImmediate
  • Microtasks: MutationObserver or process.nexttick

2.2.5. The then method can be called multiple times

  • After the promise state becomes a big pity, all the ondepressing callbacks need to be executed in the order of THEN, that is, in the order of registration
  • After the promise status changes to Rejected, all onRejected callbacks need to be executed in the order of THEN, that is, in the order of registration

2.2.6. The return value of then

The return value for then should be a new Promise

promise2 = promise1.then(onFulfilled, onRejected);
Copy the code
  • OnFulfilled or onRejected execution result is x, call resolvePromise (promise2, x, resolve, reject)
  • If onFulfilled or onRejected executes an exception, promise2 needs to reject the exception
  • If the ondepressing is not a function, promise2 triggers the depressing with the value of promise1
  • If onRejected is not a function, promise2 triggers the Rejected function with the reason of promise1

2.2.7. ResolvePromise

resolvePromise(promise2,x,resolve,reject);
Copy the code
  • If promise2 is the same as X, reject a type exception “TypeError or an infinite loop”

  • If x is a promise

    • If X is in the pending state, the promise must continue to wait until x becomes a pity/Rejected
    • If the X is fulfilled during the depressing state, the promise will be fulfilled with the same value
    • If x is in the Rejected state, the Promise rejects for the same reason
  • If x is an object or function

      1. let then=x.then
      1. If x. Chen fails, then promise rejects for the same reason
      1. If then is a function, then the call (x, resolvePromiseFn, rejectPromiseFn)
      • ResolvePromiseFn into is y, perform resolvePromise (promise2, y, resolve, reject)
      • Reject (r) with r as its input parameter.
      • If both resolvePromiseFn and rejectPromiseFn are invoked, the first call takes precedence and subsequent calls are ignored
      1. If then executes an exception
      • If resolvePromiseFn or rejectPromiseFn is already invoked, ignore it
      • Reject Exception thrown
  • If then is a normal value, resolve(x) is straightforward;

3. The implementation

3.1. Define three states

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
Copy the code

3.2.class implements a Promise

  • 1. New Promise is passing a constructor “executor”
  • 2. Executor is executed by default and takes resolve and reject
  • The resolve function, which handles wait state to success state and assigns value
  • Reject (reject); reject (reject); reject (reject)
  • 5. A reject exception is required when an error occurs during executor execution
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class Promise {
    constructor(executor) {
        // Initialization state
        this.state = PENDING;
        // Success is a value
        this.value = null;
        // Failure value
        this.reason = null;

        const resolve = (value) = > {
            if (this.state === PENDING) { //pending->resolve->fulfilled
                this.state = FULFILLED;
                this.value = value; }}//pending->reject(reason)->rejected
        const reject = (reason) = > {
            if (this.state === PENDING) {
                this.state = REJECTED;
                this.reason = reason; }}try { //pending->throw new Error->rejected
            executor(resolve, reject);
        } catch(error) { reject(error); }}}Copy the code

3.3. Then the method

3.3.1. OnFulfilled onRejected

  • OnFulfilled,onRejected
  • Ondepressing is not a function, the value will pass down the “value penetration”.
  • The onRejected is not a function the value passes down the value pass down
then(onFulfilled,onRejected){
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val= > val;
    onRejected = typeof onRejected === 'function' ? onRejected : e= > {
            throw e
        }
}
Copy the code

The return value of 3.3.2. Then is a new PROMsie

If the return value of then is not a new promise, there is a problem with the violation that once success or failure is final, the subsequent THEN can’t be processed

then(onFulfilled,onRejected){
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val= > val;
    onRejected = typeof onRejected === 'function' ? onRejected : e= > {
            throw e
        }
    const promise2=new Promise((resolve,reject) = >{})
    return promise2;    
}
Copy the code

3.3.3. Handle different states

  • This is a pity: the process of immediate success
  • REJECTED: Indicates the processing when you fail immediately
then(onFulfilled,onRejected){
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val= > val;
    onRejected = typeof onRejected === 'function' ? onRejected : e= > {
            throw e
        }
    const promise2=new Promise((resolve,reject) = >{
        switch (this.state) {
            case FULFILLED:
                onFulfilled(this.value);
                break;
            case REJECTED:
                onRejected(this.reason);
                break; }})return promise2;    
}
Copy the code

3.3.4. Wait state processing

This is a pity or Rejected function. This is a pity or rejected function. This is a pity or rejected function. At this time, the state is essentially in the waiting state, so we need a monitoring mechanism of the state. When the state becomes a pity or Rejected, we will perform callback. For this, we adopt the publish and subscribe mode

  • 1. Create a _led_callback_list to store successful callbacks
  • 2. Create a _rejected_callback_list to store failed callbacks
  • 3. Execute the subscription loop when pending->resolve-> depressing
  • 4. Loop through pending-> Reject -> Rejected
const promise2=new Promise((resolve,reject) = >{
    switch (this.state) {
        case FULFILLED:
            onFulfilled(this.value);
            break;
        case REJECTED:
            onRejected(this.reason);
            break;
        case PENDING:
            this._fulfilled_callback_list.push(
            () = > {
                onFulfilled(this.value);
            })
            this._rejected_callback_list.push(
            () = > {
                ronRejected(this.reason); })}})Copy the code
// this is a big pity
this._fulfilled_callback_list = [];
// Store the wait state onRejected
this._rejected_callback_list = [];
const resolve = (value) = > {
    //may transition to either the fulfilled or
rejected state.
    if (this.state === PENDING) { //
pending->resolve->fulfilled
        this.value = value;
        this.state = FULFILLED;
        this._fulfilled_callback_list.forEach
(cb= >cb()); }}//pending->reject(reason)->rejected
const reject = (reason) = > {
    //may transition to either the fulfilled or
rejected state.
    if (this.state === PENDING) {
        this.reason = reason;
        this.state = REJECTED;
        this._rejected_callback_list.forEach(cb= >cb()); }}Copy the code

3.3.5. Promise2

  • This is a big pity, onRejected may be wrong, promise2 must refuse to perform
  • OnFulfilled, onFulfilled return value, which may be of any type “Promise…” ResolvePromise is required to handle different cases
  • 3. ResolvePromise (promise2, x, resolve, reject) the promise2 may not exist, the need to deal with “here” is realized by using setTimeout asynchronous
//A promise must provide a then method to access its current or eventual value or reason
then(onFulfilled, onRejected) {
    //If onFulfilled is not a function, it must be ignored.
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val= > val;
    //If onRejected is not a function, it must be ignored.
    onRejected = typeof onRejected === 'function' ? onRejected : e= > {
        throw e
    }
    const promise2 = new Promise((resolve, reject) = > {
        let realOnFulfilled = () = > {
            setTimeout(() = > { //onFulfilled or onRejected must not be called until the execution 
context stack contains only platform code
                try {
                    //If either onFulfilled or onRejected returns a value x, run the Promise 
Resolution Procedure [[Resolve]](promise2, x)
                    const x = onFulfilled(this.value);
                    resolvePromise(promise2, x, resolve, reject);
                } catch(error) { reject(error); }},0);
        }
        let realOnRejected = () = > {
            setTimeout(() = > { //onFulfilled or onRejected must not be called until the execution 
context stack contains only platform code
                try {
                    const x = onRejected(this.reason);
                    resolvePromise(promise2, x, resolve, reject);
                } catch(error) { reject(error); }},0);
        }
        switch (this.state) {
            case FULFILLED:
                realOnFulfilled();
                break;
            case REJECTED:
                realOnRejected();
                break;
            case PENDING:
                this._fulfilled_callback_list.push(() = > {
                    realOnFulfilled();
                })
                this._rejected_callback_list.push(() = > {
                    realOnRejected();
                })
                break; }});return promise2;
}
Copy the code

3.3.6. ResolvePromise

  • 1. X is the same as promise2 in an infinite loop
  • 2. X is a Promise. “Here is my Promise”
  • 3. X is an object or function
    • 3.1.x has the then method, indicating that it is a thenable
    • 3.2. X without THEN indicates a common object or NULL
  • 4. X is a common value
function resolvePromise(promise2, x, resolve, reject) {
    //If promise and x refer to the same object, reject promise with a TypeError as the reason.
    if (promise2 === x) {
        reject(new TypeError('Dead loop'))}else if (x instanceof Promise) { //If x is a promise, adopt its state
        x.then(y= > {
            resolvePromise(promise2, y, resolve, reject);
        }, reject);
    } else if ((typeof x === 'object'&& x ! = =null) | | (typeof x === 'function')) { //Otherwise, if x is an object or function
        let called = false;
        try {
            //Let then be x.then
            let then = x.then;
            if (typeof then === 'function') { //If then is a function
                //call it with x as this
                then.call(x, y= > { //first argument resolvePromise
                    if (called) return;
                    called = true;
                    //If/when resolvePromise is called with a value y, run [[Resolve]](promise, y)
                    resolvePromise(promise2, y, resolve, reject);
                }, r= > { //second argument rejectPromise
                    if (called) return;
                    called = true;
                    reject(r); //If/when rejectPromise is called with a reason r, reject promise with r})}else { //If then is not a function, fulfill promise with x.resolve(x); }}catch (error) { //If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason
            if (called) return;
            called = true; reject(error); }}else { //If x is not an object or function, fulfill promise with xresolve(x); }}Copy the code

3.3.7. Test the promise of implementation

Now that we’ve implemented A Promise according to the Promise A+ specification, let’s test the Promise we wrote

npm install promises-aplus-tests -g
Copy the code
Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise((resolve, reject) = > {
        dfd.resolve = resolve;
        dfd.reject = reject;
    })
    return dfd;
}
Copy the code
promises-aplus-tests promise.js
Copy the code

3.4. The catch

catch (onRejected) {
    return this.then(null, onRejected);
}
Copy the code

3.5. Promise. Resolve to achieve

  • 1. The resolve method is static
  • 2. The resolve argument can be of any type
  • 3. The result of resolve is a promise
static resolve(value) {
    if (value instanceof Promise) return value;
    return new Promise(resolve= >{ resolve(value); })}Copy the code

3.6. Promise. Reject

  • 1. Reject is a static method
  • 2. Reject can be any type
  • 3. Reject returns a promise
static reject(reason) {
    return new Promise((_, reject) = >{ reject(reason); })}Copy the code

3.7. Promise. All implementations

The problem of asynchronous concurrency can be solved and the results returned are stored in the order in which they were called. Success only after all success otherwise failure logic is executed

  • 1. The all method is static
  • 2. The parameter of all can be any type of array
  • 3. All returns a promise
static all(promiseList) {
    return new Promise((resolve, reject) = > {
        let len = promiseList.length,
            timers = 0;
        result = [];
        const resolveResult = (value, index) = > {
            result[index] = value;
            if(++timers === len) { resolve(result); }}for (let i = 0; i < len; i++) {
            const value = promiseList[i];
            if (isPromise(value)) {
                value.then(x= > {
                    resolveResult(x, i);
                }, reject)
            } else{ resolveResult(value, i); }}})}Copy the code

3.8. Promise. Race

Race problem handling, whether successful or unsuccessful, returns the first value returned

  • 1. The RACE method is static
  • 2. Race parameters can be any type of array
  • 3. The return result of race is a promise
static race(promiseList) {
    return new Promise((resolve, reject) = > {
        let len = promiseList.length;
        if (len === 0) resolve();
        else {
            for (let i = 0; i < len; i++) {
                Promise.resolve(promiseList[i]).then
(value= > {
                    resolve(value)
                }, reason= >{ reject(reason); })}}})}Copy the code

3.9. The finally realized

  • 1. Finally callback has no arguments
  • 2 finally returns a promise
  • 3. Finally executes regardless of success or failure
  • 4. Finally successful values are not used as values for the next THEN
  • 5. Finally, a failed value will end the chain, and its failed value will be used as the value of the next then
finally(callback) { // Callback has no arguments
    return this.then(data= > { // Whether you succeed or failCallback will execute// Callback executes, possibly a promise that needs to wait until it finishes executing// Success requires passing the then value, not the callback value
        return Promise.resolve(callback()).then(_= > data);
    }, error= > {
        Resolve needs to be called because reject does not wait in thenThrow out the error valuereturn Promise.resolve(callback()).then(_= > {
            throw error
        });
    });
}
Copy the code