【js进阶9】promise原理

JavaScript | 2020-09-28 20:27:53 174次 4次

上篇通过 promise A+ 规范进行了代码举例,本次进行一个 promise 模拟实现,具体实现代码放在这里

测试库:

npm i -g promises-aplus-tests
promises-aplus-tests Promise.js


一、Promise 模拟实现

1、定义

// promise 三个状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

function Promise(excutor) {
    let that = this; // 缓存当前promise实例对象
    that.status = PENDING; // 初始状态
    that.value = undefined; // fulfilled状态时 返回的信息
    that.reason = undefined; // rejected状态时 拒绝的原因
    that.onFulfilledCallbacks = []; // 存储fulfilled状态对应的onFulfilled函数
    that.onRejectedCallbacks = []; // 存储rejected状态对应的onRejected函数

    function resolve(value) { // value成功态时接收的终值
        // resolve的值是一个promise,2.3.2
        if(value instanceof Promises) {
            return value.then(resolve, reject);
        }
        // 模拟微任务
        setTimeout(() => {
            // 调用resolve 回调对应onFulfilled函数
            if (that.status === PENDING) {
                // 只能由pedning状态 => fulfilled状态 (避免调用多次resolve reject)
                that.status = FULFILLED;
                that.value = value;
                that.onFulfilledCallbacks.forEach(cb => cb(that.value));
            }
        });
    }

    function reject(reason) { // reason失败态时接收的拒因
        setTimeout(() => {
            // 调用reject 回调对应onRejected函数
            if (that.status === PENDING) {
                // 只能由pedning状态 => rejected状态 (避免调用多次resolve reject)
                that.status = REJECTED;
                that.reason = reason;
                that.onRejectedCallbacks.forEach(cb => cb(that.reason));
            }
        });
    }

    // 捕获在excutor执行器中抛出的异常
    // new Promise((resolve, reject) => {
    //     throw new Error('error in excutor')
    // })
    try {
        excutor(resolve, reject);
    } catch (e) {
        reject(e);
    }
}


2、then

Promises.prototype.then = function(onFulfilled, onRejected) {
    const that = this;
    let newPromise;
    // 2.2.7.3 | 2.2.7.4 透传
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
    onRejected = typeof onRejected === "function" ? onRejected : reason => {
        throw reason;
    };

    if (that.status === FULFILLED) { // 成功态
        return newPromise = new Promise((resolve, reject) => {
            setTimeout(() => {
                try{
                    let x = onFulfilled(that.value);
                    // 新的promise resolve 上一个onFulfilled的返回值
                    resolvePromise(newPromise, x, resolve, reject);
                } catch(e) {
                    // 捕获前面onFulfilled中抛出的异常 then(onFulfilled, onRejected);
                    reject(e); 
                }
            });
        })
    }

    if (that.status === REJECTED) { // 失败态
        return newPromise = new Promise((resolve, reject) => {
            setTimeout(() => {
                try {
                    // 2.2.7.1
                    let x = onRejected(that.reason);
                    resolvePromise(newPromise, x, resolve, reject);
                } catch(e) {
                    reject(e);
                }
            });
        });
    }

    if (that.status === PENDING) { // 等待态
        // onFulfilled/onRejected 收集
        return newPromise = new Promise((resolve, reject) => {
            that.onFulfilledCallbacks.push((value) => {
                try {
                    // 2.2.7.2
                    let x = onFulfilled(value);
                    resolvePromise(newPromise, x, resolve, reject);
                } catch(e) {
                    reject(e);
                }
            });
            that.onRejectedCallbacks.push((reason) => {
                try {
                    let x = onRejected(reason);
                    resolvePromise(newPromise, x, resolve, reject);
                } catch(e) {
                    reject(e);
                }
            });
        });
    }
};



3、解析过程

function resolvePromise(promise2, x, resolve, reject) {
    // 2.3.1
    if (promise2 === x) {  // 如果从onFulfilled中返回的x 就是promise2 就会导致循环引用报错
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
    }

    let called = false; // 避免多次调用 2.2.2.3
    // 如果x是一个promise对象 2.3.2
    if (x instanceof Promises) { // 获得它的终值 继续resolve
        if (x.status === PENDING) { // 如果为等待态需等待直至 x 被执行或拒绝 并解析y值
            x.then(y => {
                resolvePromise(promise2, y, resolve, reject);
            }, reason => {
                reject(reason);
            });
        } else { // 如果 x 已经处于执行态/拒绝态(值已经被解析为普通值),用相同的值执行传递下去 promise
            x.then(resolve, reject);
        }
    // 如果 x 为对象或者函数 2.3.3
    } else if (x != null && ((typeof x === 'object') || (typeof x === 'function'))) {
        try { // 是否是thenable对象(具有then方法的对象/函数)
            let then = x.then;
            if (typeof then === 'function') {
                then.call(x, y => {
                    if(called) return;
                    called = true;
                    resolvePromise(promise2, y, resolve, reject);
                }, reason => {
                    if(called) return;
                    called = true;
                    reject(reason);
                })
            } else { // 说明是一个普通对象/函数 return {then:5}
                resolve(x);
            }
        } catch(e) {
            if(called) return;
            called = true;
            reject(e);
        }
    } else {
        // 2.3.4
        resolve(x);
    }
}


4、race

race 是一个静态方法,仅执行最快的那个,利用【2.2.2.3】特性

Promises.race = function(promises) {
    return new Promises((resolve, reject) => {
        promises.forEach((promise, index) => {
           // 最快的执行完之后,后面的 resolve | reject 不生效
           promise.then(resolve, reject);
        });
    });
}


5、all

Promises.all = function(list) {
    return new Promise((resolve, reject) => {
        let resValues = [];
        let counts = 0;
        list.forEach((p, i) => {
	    p.then(res => {
	        counts++;
		resValues[i] = res;
		if (counts === list.length) {
		    resolve(resValues)
		}
	    }, err => {
	        reject(err)
	    })
	})
    })
}

但是 all 此时的方法有问题,比如其中一个失败,则成功的结果也得不到,都进到了 catch 里面,需要进行优化下:

function handlePromise(promiseList) {
    return promiseList.map(promise =>
      promise.then((res) => ({ status: 'ok', res }), (err) => ({ status: 'not ok', err }))
    )
}

Promise.allSettled = function (promiseList) {
    return Promise.all(handlePromise(promiseList))
}

 Promise.allSettled ([
      Promise.reject(1),
      Promise.resolve(2),
      Promise.resolve(3)
  ]).then(res => console.log(res), err => console.log(err))


6、finally

示例:
let promise = new Promise((resolve, reject) => {
    reject('this is error')
})
promise.then((value) => {
    console.log('fulfill', value)
}).finally(res => {
    // undefined  end
    console.log(res, 'end')
}).catch(err=>{
    // this is error   reject
    console.log(err, 'reject')
})
实现:
Promises.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    //xxx.then(() => value)  -- finally后可以继续 .then 等
    value  => P.resolve(callback()).then(() => value),
    // reject 抛出错误 继续 catch 可以捕获
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};


7其他

// 直接 reject
Promises.prototype.catch = function(onRejected) {
    return this.then(null, onRejected);
}

//静态方法 resolve
Promises.resolve = function (value) {
    return new Promises(resolve => {
        resolve(value);
    });
}

// 静态方法 reject 
Promises.reject = function (reason) {
    return new Promises((resolve, reject) => {
        reject(reason);
    });
}


二、可取消的Promise

function getWithCancel(promise, token) {
   let handle = {}
   Promise.race([promise, new Promise((_, reject) => {
     handle.cancel = function() {
       reject(new Error('cancel'))
     }
   })]).catch((e) => console.log(e))
   return handle;
};


var promise = fetch(xxxxx)
getWithCancel(promise).cancel()


三、async/await原理

async/await 是 promise 和 Generator 语法糖。参考

async function fn(args) {
  // ...
}

// 等同于
function fn(args) {
  return spawn(function* () {
    // ...
  });
}
function spawn(genF) {
  return new Promise(function(resolve, reject) {
    const gen = genF();
    function step(nextF) {
      let next;
      try {
        next = nextF();
      } catch(e) {
        return reject(e);
      }
      if(next.done) {
        return resolve(next.value);
      }
      Promise.resolve(next.value).then(function(v) {
        step(function() { return gen.next(v); });
      }, function(e) {
        step(function() { return gen.throw(e); });
      });
    }
    step(function() { return gen.next(undefined); });
  });
}


4人赞

分享到: