PromisA + specification

First, I brought in some specifications, just to get a sense of it

The term

  1. Promise ———— is an object or function that has then methods and behaves according to this specification
  2. Thenable ———— is an object or function that has then methods
  3. Value ———— is the value of the successful promise state. Resolve (value) is a string, number, Boolean, undefined, thenable, promise…
  4. Reason ———— is the value of the promise state when it fails, reject(reason)
  5. Exception ———— is an exception thrown with throw

specification

Promise States

Promises come in three states.

1. pending
This will be a pity state. 1.2 A promise will be in this state before resolve or reject. 1.4 Reject -- > Reject;Copy the code
2. fulfilled
2.1 Final state, immutable 2.2 A promise becomes this state when resolved 2.3 Must have a valueCopy the code
3. rejected
3.1 Final State, immutable. 3.2 A Promise becomes this state when it is rejected. 3.3 There must be a ReasonCopy the code

Pity pending -> reject(reason) -> Pity pending -> reject(reason) -> rejected

then

A Promise should provide a THEN method that accesses the end result, either value or Reason. promise.then(onFulfilled, onRejected)

1. Parameter specifications
Onpity must be a function type, which should be ignored if the passed function is not a function (there will be a default value).Copy the code
2. OnFulfilled characteristics
2.1 Ondepressing should be called when the promise becomes a big pity, and the parameter is value (question: The implementation time of ondepressing?). 2.2 Promises should not be called before this is a big pity. 2.3 Promises can only be called once (so a variable is needed to limit the number of times when they are implemented)Copy the code
3. OnRejected features
3.1 When promise becomes Rejected, OnRejected should be called with reason 3.2 it should not be called until the promise is rejected. 3.3 It can only be called once (so you need a variable to limit the number of times it is implemented).Copy the code
4. OnFulfilled and onRejected should be implemented in the micro-task stage
How do you generate microtasks when you implement promises?Copy the code
5. The then method can be called multiple times
5.1 After the promise state becomes a pity, This is a big pity. All the onFulfilled callback needs to be implemented according to the order of THEN, which is also the order of registration (so an array is needed to store multiple onFulfilled callback). 5.2 After the Promise state becomes Rejected, All onRejected callbacks need to be executed in the order of THEN, that is, in the order of registration.Copy the code
6. The return value

Promise1 = promise1. Then (onFulfilled, onRejected);

6.1 The result of onFulfilled or onRejected is X, and resolvePromise is called. 6.2 If exception E is thrown when onFulfilled or onRejected is implemented, This is the big pity. 6.3 If onFulfilled is not a function, promise2 will trigger the fulfilled function with the value of promise1. 6.4 If onFulfilled is not a function, Promise2 Triggers Rejected with the reason of promise1Copy the code
7. resolvePromise

resolvePromise(promise2, x, resolve, reject);

Reject a TypeError if x is a promsie: This is a big pity or rejected. If X is pending, then the promise must be pending until the state of X changes gradually or gradually. This is a big pity, value -- > pity if X is fulfilled, reason -- > rejected 7.3 If X is an object or a function: Let then = x.testen. // get x.testen if x.testen = rejected, reason -- > rejected if then = x.testen Then. Call (x, resolvePromiseFn, rejectPromise) reject); R, reject promise with r. If both resolvePromise and rejectPromise are invoked, the first invocation takes precedence over the subsequent invocation. If the call to then throws an exception e if resolvePromise or rejectPromise has already been called, then ignore, Reject promise with eas the reason if then is not a function.Copy the code

Fulfill a Promise

Then, step by step, implement a promise according to the complex specification above

1. Normally a promise is new promise () with the new keyword. This time it is implemented with a constructor or class

2. Define three state types

3. Initialize state — > Pending

4. Resolve and Reject

4.1 These two methods need to change the status from pending to pity/Rejected

5. Handling of input parameters when instantiating promise

5.1 An input parameter is a function that accepts resolve and reject. 5.2 When a promise is initialized, this function is executed synchronously, and any errors are thrown through Reject

6. Then method

This will be a pity and onFulfilled. If the parameter is not a function, this will be ignored. 6.3 Call different functions according to the current promise state. Create two new arrays to store the successful and failed callbacks respectively. Then is called, and if pending is called, store the array. 6.5 Execute the callback when the status changes, and use the getter/setter to listen for the status change and perform the corresponding operations

7. Return value of then

7.1 If onFulfilled or onRejected throws exception E, then the new promise must reject this exception. If onRejected is not a function, promise2 must return the same status and value. 7.4 If onRejected is not a function, promise2 must reject and return the same data. 7.5 If onFulfilled or onRejected returns a value x, then run the resolvePromise method

8. resolvePromise

Reject typeError if x is a promise: <1> If x is pending, the promise must be in the pengding state until the x state changes. <2> If x is fulfilled, value — > fulfilled <3> If x is fulfilled, Call (x, reject) = reject (x, reject) = reject (x, reject) resolvePromisFn, rejrctPromiseFn)

This is a big onpity and onRejected micro task

// 2.定义三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

 // 1. 先声明一个MPromise
class MPromise {   
     // 新建两个数组,分别存储成功和失败的回调
    FULFILLED_CALLBACK_LIST = [] 
    REJECTED_CALLBACK_LIST = []
    _status = PENDING   // 用一个新的变量来存储状态
    constructor(fn) {   // 入参 传入的参数立执行
    // 3. 初始化状态和一些初始值
        this.status = PENDING;
        this.value = null;
        this.reason = null;

         try {
              // 5. 初始化的时候执行函数, 传入两个参数
            fn(this.resolve.bind(this), this.reject.bind(this));
        } catch (e) {
             // 报错直接reject
            this.reject(e);
        }
    }
    
    // 6.5 getter / setter 截持 status
    get status() {
        return this._status;  //所有真实的状态都存在这个_status中
    }
    
    set status(newStatus) {
        this._status = newStatus;
        // status 发生变化时, 执行回调
        switch (newStatus) {
            case FULFILLED: {
                // 按照注册的顺序遍历执行
                this.FULFILLED_CALLBACK_LIST.forEach(callback => {
                    callback(this.value);
                });
                break;
            }
            case REJECTED: {
                this.REJECTED_CALLBACK_LIST.forEach(callback => {
                    callback(this.reason);
                });
                break;
            }
        }
    }
    
    // 4.resolve 方法
     resolve(value) {
         // 更改状态, 赋上value
        if (this.status === PENDING) {  // 最终状态只能从pending改变而来
            this.value = value;
            this.status = FULFILLED;
        }
    }
    
     // reject 方法
    reject(reason) {
         // 更改状态, 赋上reason
        if (this.status === PENDING) {
            this.reason = reason;
            this.status = REJECTED;
        }
    }
    
    // 6. then方法, 返回值为promis
    then(onFulfilled, onRejected) {
        // 6.2检查一下的参数,如果是函数,就用此函数, 如果不是,就声明一个函数,返回value
       const fulFilledFn = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
        return value;
    };
        // 同样的处理, 如果不是,就生成一个函数,抛出错误
      const rejectedFn = this.isFunction(onRejected) ? onRejected : (reason) => {
        throw reason;
    }; 
    // 注:在后面的代码会重新判断onFulfilled 和onRejected 是否为函数, 这里的判断在后面的代码中可以有更简洁的写法
    
        // 7.1封装带有try/ catch 的函数, 在onFulfilled 或onRejected 发生异常的时候进行处理
        const fulFilledFnWithCatch = (resolve, reject, newPromise) => {
            // 9. 在微任务中调用函数
            queueMicrotask(()=>{
                 try {
                   if (!this.isFunction(onFulfilled)) {
                    // 7.3 如果 onFulfilled 不是函数, promis1成功,promis2即返回同样的状态和value
                    resolve(this.value);
                } else {
                    // 7.5 如果onFulfilled是函数,拿到返回的值为 x, 则执行resolvePromise
                    const x = fulFilledFn(this.value);
                    //还要传入一个newPromise,由前面传入
                    this.resolvePromise(newPromise, x, resolve, reject);
                }
                } catch (e) {
                    reject(e)
                }
            }) 
        };
        const rejectedFnWithCatch = (resolve, reject,newPromise) => {
            queueMicrotask(()=>{
                 try {
                    rejectedFn(this.reason);
                    // 需要注意的是,如果promise1的onRejected执行成功了,promise2应该被resolve
                     if (!this.isFunction(onRejected)) {
                     // 如果 onRejected 不是函数, promise2 必须拒绝执行并返回相同的据因。
                        reject(this.reason);
                    } else {
                        // 7.5 如果onRejected 是函数,拿到返回值 x, 并执行resolvePromise
                        const x = rejectedFn(this.reason);
                        this.resolvePromise(newPromise, x, resolve, reject);
                    }
                } catch (e) {
                    reject(e);
                }
            })
        }

        // 根据promise状态调用不同的函数
        switch(this.status) {
            // 如果成功, 就调用fulFilledFn, 传出value,
          case FULFILLED: {
            //fulFilledFn(this.value);  这里就空可以执行封装了带有异常处理的fulFilledFnWithCatch, then返回的是一个promise, 定义一个newPromise
            const newPromise = new MPromise((resolve, reject)=> fulFilledFnWithCatch(resolve, reject, newPromise));
            return newPromise;
        }
            // 如果失败, 就调用rejectedFn, 传出reason
        case REJECTED: {
            //rejectedFn(this.reason);  这里就空可以执行封装了带有异常处理的rejectedFnWithCatch,then返回的一个promise
            const newPromise = new MPromise((resolve, reject)=> rejectedFnWithCatch(resolve, reject,newPromise));
            return newPromise;
        }
        // 如果有异步的操作,status还没变成fulfilled或者rejected, 很有可能还是pending的
         case PENDING: {
         const newPromise = new MPromise((resolve, reject) => {
                 this.FULFILLED_CALLBACK_LIST.push(() => fulFilledFnWithCatch(resolve, reject, newPromise));
                 this.REJECTED_CALLBACK_LIST.push(() => rejectedFnWithCatch(resolve, reject, newPromise));
             })
            return newPromise;
        }
        }
    }
    
    //实现一个catch方法
    catch(onRejected){
        return this.then(null, onRejected);
    }
    
    //8. 定义一个resolvePromise, 有4个参数
    resolvePromise(newPromise, x, resolve, reject) {
        // 如果 newPromise 和 x 指向同一对象,会相互调用,导致死循环
        if (newPromise === x) {
            // 所以TypeError 为据因拒绝执行 newPromise,返回错误
            return reject(new TypeError('The promise and the return value are the same'));
        }
        
         // 如果 x 为 Promise ,则使 newPromise 接受 x 的状态
        if (x instanceof MPromise) {
            // 也就是继续执行x,如果执行的时候拿到一个y,还要继续解析y
            // 这个if跟下面判断then然后拿到执行其实重复了,可有可无
            x.then((y) => {
                resolvePromise(newPromise, y, resolve, reject);
            }, reject);
          // 如果 x 为对象或者函数
        } else if (typeof x === 'object' || this.isFunction(x)) {
            // 这个坑是跑测试的时候发现的,如果x是null,应该直接resolve
            if (x === null) {
                return resolve(x);
            }

            let then = null;

            try {
                // 把 x.then 赋值给 then 
                then = x.then;
            } catch (error) {
                // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
                return reject(error);
            }

        // 如果 then 是函数
        if (this.isFunction(then)) {
            let called = false;
            // 将 x 作为函数的作用域 this 调用之
            // 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
            // 名字重名了,我直接用匿名函数了
            try {
                then.call(
                    x,
                    // 如果 resolvePromise 以值 y 为参数被调用,则运行 resolvePromise
                    (y) => {
                        // 如果 resolvePromise 和 rejectPromise 均被调用,
                        // 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
                        // 实现这条需要前面加一个变量called
                        if (called) return;
                        called = true;
                        resolvePromise(promise, y, resolve, reject);
                    },
                    // 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
                    (r) => {
                        if (called) return;
                        called = true;
                        reject(r);
                    });
            } catch (error) {
                // 如果调用 then 方法抛出了异常 e:
                // 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
                if (called) return;

                // 否则以 e 为据因拒绝 promise
                reject(error);
            }
        } else {
            // 如果 then 不是函数,以 x 为参数执行 promise
            resolve(x);
        }
    } else {
        // 如果 x 不为对象或者函数,以 x 为参数执行 promise
        resolve(x);
    }
}
    
    //判断是不是函数的方法
    isFunction (param) {
        return typeof param === 'function';
    }
}

//const promise = new MPromise((resolve, reject) => {});
Copy the code
Finally, run the test

conclusion

Basically, it is implemented step by step in accordance with the promise specification, which is still quite convoluted in the middle and needs to be read several times. There was a bit of a mix-up

After checking the code, I found that the node version was too low, which caused the syntax error. After upgrading the Node, it ran normally.