Promise/A+ 规范手写 promise 实现 then、catch、resolve、reject、finally、all、race

125 阅读6分钟

在看本文章之前,需要熟悉 promise 的使用,不了解 promise 使用的同学,可以先去学习 es6——promise

promise/A+ 规范

1. 初识 promise

首先我们知道 promise 是一个构造函数,这里我们用 es6 新特性 class 类来创建一个 promise。

class Promise {
    constructor() {
        
    }
}

module.exports = Promise;

我们在使用 promise 的时候知道要先创建 promise 实例,然后传入一个函数,函数里包括 resolve、reject 两个参数

var promise = new Promise((resolve, reject) => {

})

像是这样,那么我们就要在构造函数中接受这个函数,这里我们把函数参数命名为 excutor

class Promise {
    constructor(excutor) {
        excutor(resolve, reject)
    }
}

module.exports = Promise;

2. promise 状态

让我们首先看一下 Promise/A+ 规范中要求

  1. promise 需要有三个状态 pendingfulfilledrejected ,默认状态是 pending
  2. 当时 fulfilled 成功状态时,promise 不能转换为其他状态了,而却必须有一个返回值
  3. 当时 rejected 失败状态时,promise 不能转换为其他状态了,而却必须有一个失败原因

所以我们的 promise 构造函数现在是这样

const PENDING = 'PENDING';
const RESOLVE = 'RESOLVE';
const REJECTED = 'REJECTED';
class Promise {
    constructor(excutor) {
        this.state = PENDING;
        this.value = undefined;
        this.reason = undefined;
        let resolve = (value) => {
            if (this.state === PENDING) {
                this.state = RESOLVE;
                this.value = value;
            }
        }
        let reject = (reason) => {
            if (this.state === PENDING) {
                this.state = REJECTED;
                this.reason = reason;
            }
        }
        excutor(resolve, reject)
    }
}

module.exports = Promise;

3. promise then 方法

让我们看一下 promise/A+ 中的介绍 一个 promise 必须提供 then 方法来访问当前或最终的结果或原因 一个 promise 方法接收两个参数 onFulfilled, onRejected

const PENDING = 'PENDING';
const RESOLVE = 'RESOLVE';
const REJECTED = 'REJECTED';
class Promise {
    constructor(excutor) {
        this.state = PENDING;
        this.value = undefined;
        this.reason = undefined;
        let resolve = (value) => {
            if (this.state === PENDING) {
                this.state = RESOLVE;
                this.value = value;
            }
        }
        let reject = (reason) => {
            if (this.state === PENDING) {
                this.state = REJECTED;
                this.reason = reason;
            }
        }
        excutor(resolve, reject)
    }

    then(onFulfilled, onRejected) {
    
    // onFulfilled如果不是函数,就忽略onFulfilled,直接返回value
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    // onRejected如果不是函数,就忽略onRejected,直接扔出错误
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
    
        if(this.state === RESOLVE) {
            onFulfilled(this.value);
        }

        if(this.state === REJECTED) {
            onRejected(this.reason);
        }
    }
}

module.exports = Promise;

到这里已经实现了同步 promise ,下面我们来实现异步 promise

4. 异步 promise 实现

当我们在 promise 里使用异步的时候 state 状态会一直是 pending 状态,这里我们可以先把成功和失败的函数先存到对应的数组中,等状态发生变化(resolve, reject)时我们在遍历调用,这就是一个发布订阅思想

const PENDING = 'PENDING';
const RESOLVE = 'RESOLVE';
const REJECTED = 'REJECTED';
class Promise {
    constructor(excutor) {
        this.state = PENDING;
        this.value = undefined;
        this.reason = undefined;
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        let resolve = (value) => {
            if (this.state === PENDING) {
                this.state = RESOLVE;
                this.value = value;
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        }
        let reject = (reason) => {
            if (this.state === PENDING) {
                this.state = REJECTED;
                this.reason = reason;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
        excutor(resolve, reject)
    }

    then(onFulfilled, onRejected) {

    // onFulfilled如果不是函数,就忽略onFulfilled,直接返回value
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    // onRejected如果不是函数,就忽略onRejected,直接扔出错误
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
    
        if(this.state === RESOLVE) {
            onFulfilled(this.value);
        }

        if(this.state === REJECTED) {
            onRejected(this.reason);
        }

        if(this.state === PENDING) {
            this.onResolvedCallbacks.push(() => onFulfilled(this.value))
            this.onRejectedCallbacks.push(() => onRejected(this.reason))
        }
    }
}

module.exports = Promise;

4. promise 链式调用

解决完异步操作,我们来看看链式调用,当我们使用 promise 的时候会用到链式调用 then 方法来解决回调地狱,那么这是怎么实现的呢,其实就是我们 then 函数的返回值是一个新的 promise2 这样递归,我们就可以实现链式调用。

    then(onFulfilled, onRejected) {

    // onFulfilled如果不是函数,就忽略onFulfilled,直接返回value
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    // onRejected如果不是函数,就忽略onRejected,直接扔出错误
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };

        let promise2 = new Promise((resolve, reject) => {
            if(this.state === RESOLVE) {
                let x = onFulfilled(this.value);
                resolve(x);
            }
    
            if(this.state === REJECTED) {
                let x = onRejected(this.reason);
                reject(x);
            }
    
            if(this.state === PENDING) {
                this.onResolvedCallbacks.push(() => {
                    let x = onFulfilled(this.value);
                    resolve(x);
                })
                this.onRejectedCallbacks.push(() => {
                    let x = onRejected(this.reason);
                    reject(x)
                })
            }
        })

        return promise2;
    }

x 的值可以是普通值,也可能是 promise ,这里我们需要判断 x 的值,这里我们写一个 resolvePromise 方法

then(onFulfilled, onRejected) {

    // onFulfilled如果不是函数,就忽略onFulfilled,直接返回value
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    // onRejected如果不是函数,就忽略onRejected,直接扔出错误
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };

        let promise2 = new Promise((resolve, reject) => {
            if(this.state === RESOLVE) {
                setTimeout(() => {
                    let x = onFulfilled(this.value);
                     // x 可能是普通纸 也可能是 promise
                     resolvePromise(promise2, x, resolve, reject);
                },0)
            }
    
            if(this.state === REJECTED) {
                setTimeout(() => {
                let x = onRejected(this.reason);
                // x 可能是普通纸 也可能是 promise
                resolvePromise(promise2, x, resolve, reject);
            },0)
            }
    
            if(this.state === PENDING) {
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                    let x = onFulfilled(this.value);
                    // x 可能是普通纸 也可能是 promise
                    resolvePromise(promise2, x, resolve, reject);
                },0)
                })
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                    let x = onRejected(this.reason);
                    // x 可能是普通纸 也可能是 promise
                    resolvePromise(promise2, x, resolve, reject);
                },0)
                })
            }
        })

        return promise2;
    }
}

由于 promise 会立即执行,导致我们不能直接获得 promise2 参数,这里我们用宏任务 setTimeOut 包裹一下,等微任务执行完才会执行宏任务,这样我们就会拿到 promise2 参数。

resolvePromise 函数

我们先看 promise/A+ 里的说明

let resolvePromise = (promise2,x,resolve,reject) => {
  // 首先 x 值不能是 promise 自己
  if(promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
  }
  // x 值是对象或者是函数
  if(typeof x === 'object' && x !== null || typeof x === 'function') {
    let called; // 防止多次调用
    try {
      let then = x.then;
      if(typeof then === 'function') {
        then.call(x, y => { // y 可能还是 promise 递归
          if(called) {
            return;
          }
          called = true;
          resolvePromise(promise2, y, resolve, reject); // 采用 promise 成功结果的值向下传递
        },r => {
          if(called) {
            return;
          }
          called = true;
          reject(r);// 采用失败结果向下传递
        })
      } else {
        resolve(x); // 说明x 是一个普通值 直接成功
      }
    } catch(e) {
      if(called) {
        return;
      }
      called = true;
      reject(e);
    }
  } else {
    resolve(x);
  }
}

到这里基本就完成了一个简单的 promise,catch、resolve、reject、finally、all、race 方法放在完整 promise 代码中,大家可以看代码理解就可以了~

5. 完整 promise

const PENDING = 'PENDING';
const RESOLVE = 'RESOLVE';
const REJECTED = 'REJECTED';

class Promise {
    constructor(executor) {
        this.state = PENDING;
        this.value = undefined;
        this.reason = undefined;
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        let resolve = (value) => {
            if (this.state === PENDING) {
                this.state = RESOLVE;
                this.value = value;
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        }
        let reject = (reason) => {
            if (this.state === PENDING) {
                this.state = REJECTED;
                this.reason = reason;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
        try {
            executor(resolve, reject)
        } catch(e) {
            reject(e)
        }
    }

    then(onFulfilled, onRejected) {
      onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : data => data;

      onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };

      let promise2 = new Promise((resolve, reject) => {
        if (this.state === RESOLVE) {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              // x 可能是普通纸 也可能是 promise
              resolvePromise(promise2, x, resolve, reject);
            } catch(e) {
              reject(e)
            }
          },0)
        }
        if (this.state === REJECTED) {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              // x 可能是普通纸 也可能是 promise
              resolvePromise(promise2, x, resolve, reject);
            } catch(e) {
              reject(e)
            }
          },0)
        }
        if (this.state === PENDING) {
          this.onResolvedCallbacks.push(() => {
            setTimeout(() => {
              try {
                let x = onFulfilled(this.value);
                // x 可能是普通纸 也可能是 promise
                resolvePromise(promise2, x, resolve, reject);
              } catch(e) {
                reject(e)
              }
            },0)
          })
          this.onRejectedCallbacks.push(() => {
            setTimeout(() => {
              try {
                let x = onRejected(this.reason);
                // x 可能是普通纸 也可能是 promise
                resolvePromise(promise2, x, resolve, reject);
              } catch(e) {
                reject(e)
              }
            },0)
          })
        }
      })
      return promise2;
    }

    catch(fn) {
      return this.then(null, fn);
    }
}

// resolve 方法
Promise.resolve = function(val){
  return new Promise((resolve,reject)=>{
    resolve(val)
  });
}

// reject 方法
Promise.reject = function(val){
  return new Promise((resolve,reject)=>{
    reject(val)
  });
}

// race 方法 
Promise.race = function(promises){
  return new Promise((resolve,reject)=>{
    for(let i=0;i < promises.length;i++){
      promises[i].then(resolve,reject)
    };
  })
}

// all 方法 
Promise.all = function(values) {
  return new Promise((resolve, reject) => {
    let arr = [];
    let index = 0;
    function processData(key, value) {
      arr[key] = value;
      if(index++ == values.length) {
        resolve(arr);
      }
    }
    for(let i = 0; i < values.length; i++) {
      let current = values[i];
      if (isPromise(current)) {
        current.then((data) => {
          processData(i, data);
        }, reject);
      }else {
        processData(i, current);
      }
    }
  })
}

// finally 方法 
Promise.prototype.finally = function(cb) {
  return this.then(data => {
    return Promise.resolve(cb()).then(() => data);
  }, err => {
    Promise.resolve(cb()).then(() => {
      throw err;
    });
  })
}

const isPromise = (value) => {
  if(typeof value === 'object' && value !== null || typeof value === 'function') {
    if (typeof value.then === 'function') {
      return true;
    }else {
      return false;
    }
  }
}

resolvePromise = (promise2,x,resolve,reject) => {
  if(promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
  }
  if(typeof x === 'object' && x !== null || typeof x === 'function') {
    let called; // 内部测试
    try {
      let then = x.then;
      if(typeof then === 'function') {
        then.call(x, y => { // y 可能还是 promise 递归
          if(called) {
            return;
          }
          called = true;
          resolvePromise(promise2, y, resolve, reject); // 采用 promise 成功结果的值向下传递
        },r => {
          if(called) {
            return;
          }
          called = true;
          reject(r);// 采用失败结果向下传递
        })
      } else {
        resolve(x); // 说明x 是一个普通的对象 直接成功
      }
    } catch(e) {
      if(called) {
        return;
      }
      called = true;
      reject(e);
    }
  } else {
    resolve(x);
  }
}

module.exports = Promise;