手写Promise核心原理

1,645 阅读4分钟

promise 可以说是出场率很高的 api 了,这篇文章手写一版 promise,可以加强对promise的认识

最后附完整代码

准备

  • 首先,promise 有三种状态:pending fulfilled rejected;

  • promise在实例化操作中, 有两个改变状态的方法,分别为resolve,reject;

  • promise有很多方法,详情请见 mdn, 本篇文章先实现 promise的核心api: thencatch;

我们使用 es6 提供的 class 来实现

class MyPromise {
 // 准备三个状态
 static PENDING = 'pending';
 static FULFILLED = 'fulfilled';
 static REJECTED = 'rejected';

 constructor(executor) {
   this.status = MyPromise.PENDING; // 表示promise的状态
   this.value = null;  // 表示promise的值
   try {
     executor(this.resolve.bind(this), this.reject.bind(this))
   } catch (error) {
     this.reject(error)
   }
 }
 resolve() {

 }
 reject() {

 }
}

在这里 executor 就是传递过来的函数,可以接收 resolvereject,这里将内部的两个方法给传入,如果在调用的过程中报错了会调用reject方法

完善 resolve/reject

他们做的工作分为以下几部

  1. 将状态改为pendingfulfilledrejected
  2. 可以接受一个值为当前的promisevalue
  resolve(value) {
    if (this.status === MyPromise.PENDING) {
      this.status = MyPromise.FULFILLED;
      this.value = value
    }
  }
  reject(value) {
    if (this.status === MyPromise.PENDING) {
      this.status = MyPromise.REJECTED;
      this.value = value
    }
  }

then

then 函数可以接受两个参数,分别为成功的回调函数和失败的回调函数,并且回调函数的默认为一个函数

  1. 状态为 fulfilled 执行第一个回调,rejected 执行第二个回调
  2. 回调函数中给传入当前的 value
  3. then的执行为异步的
  then(onFulfilled, onRejected) {
    if (typeof onFulfilled !== 'function') {
      onFulfilled = value => value
    }

    if (typeof onFulfilled !== 'function') {
      onRejected = value => value
    }

    if (this.status === MyPromise.FULFILLED) {
      setTimeout(() => {
        onFulfilled(this.value)
      })
    }

    if (this.status === MyPromise.REJECTED) {
      setTimeout(() => {
        onRejected(this.value)
      })
    }
  }

验证一下

console.log(1)
new MyPromise((resolve, reject) => {
  console.log(2)
  resolve('成功')
}).then(res => console.log(res))

console.log(3)

// 打印 1 2 3 成功

promise里面有异步代码的时候,这个时候运行到.then方法 状态为pending,下来增加一下异步任务的处理

异步处理

当状态为pending的时候,表示执行的是异步任务,这个时候我们可以增加一个callback,把异步执行的内容添加到这个callback中,当执行完异步代码的时候,会执行异步函数的callback的任务

constructor(executor) {
  // ...
  this.callbacks = []; // 用来存储回调函数的容器
  // ...
}
resolve(value) {
  // ...
  this.callbacks.forEach(({ onFulfilled }) => onFulfilled(value)) 
  // 当执行到这里的时候 如果有onFulfilled 就说明已经执行完then方法给容器添加内容了。把resolve的值传递给onFulfilled
} 
reject(value) {
  // ...
  this.callbacks.forEach(({ onRejected }) => onRejected(value))
  // 当执行到这里的时候 如果有onRejected 就说明已经执行完then方法给容器添加内容了。把reject的值传递给onFulfilled
}
then(onFulfilled, onRejected) {
  // ...
  if (this.status === MyPromise.PENDING) {
    this.callbacks.push({
      onFulfilled: value => {
        setTimeout(() => {
          onFulfilled(value)
        })
      },
      onRejected: value => {
        setTimeout(() => {
          onRejected(value)
        })
      }
    })
  }
 }

验证一下

new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('成功')
  })
}).then(res => console.log(res)) 

// 打印  成功

then 函数可以链式调用,接下来我们完善一下

链式调用

链式调用的核心就是返回一个新的 promise,当成功调用的时候调用新的promiseresolve,失败reject,并且链式调用会把前一个的返回值当作下一个的 resolve 的状态

then(onFulfilled, onRejected) {
    if (typeof onFulfilled !== 'function') {
      onFulfilled = value => value
    }

    if (typeof onFulfilled !== 'function') {
      onRejected = value => value
    }
    return new MyPromise((resolve, reject) => {
      if (this.status === MyPromise.FULFILLED) {
        setTimeout(() => {
          const result = onFulfilled(this.value)
          resolve(result)
        })
      }

      if (this.status === MyPromise.REJECTED) {
        setTimeout(() => {
          const result = onRejected(this.value)
          resolve(result) 
        })
      }

      if (this.status === MyPromise.PENDING) {
        this.callbacks.push({
          onFulfilled: value => {
            setTimeout(() => {
              const result = onFulfilled(value)
              resolve(result)
            })
          },
          onRejected: value => {
            setTimeout(() => {
              const result = onRejected(value)
              resolve(result)
            })
          }
        })
      }
    })
  }

验证一下

new MyPromise((resolve, reject) => {
  setTimeout(() => {
    reject('失败')
  })
}).then(res => res, err => err).then(res => console.log(res))

// 打印  失败

如果.then的回调函数返回的是promise的情况也要做个处理

边界处理

实现前

new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('成功')
  })
}).then(
  res => new MyPromise((resolve, reject) => {
    resolve(res)
  }),
  err => err
).then(res => console.log(res))

// 打印  { "status": "fulfilled", "value": "成功", "callbacks": [] }

当判断返回值为 MyPromise 的时候,需要手动调用 .then 的方法取他的值,并且吧当前的 promise 的改变状态的函数透出给 then 方法

  then(onFulfilled, onRejected) {
    if (typeof onFulfilled !== 'function') {
      onFulfilled = value => value
    }

    if (typeof onFulfilled !== 'function') {
      onRejected = value => value
    }
    return new MyPromise((resolve, reject) => {
      if (this.status === MyPromise.FULFILLED) {
        setTimeout(() => {
          const result = onFulfilled(this.value)
          if (result instanceof MyPromise) { 
            result.then(resolve, reject)
          } else {
            resolve(result)
          }
        })
      }

      if (this.status === MyPromise.REJECTED) {
        setTimeout(() => {
          const result = onRejected(this.value)
          if (result instanceof MyPromise) {
            result.then(resolve, reject)
          } else {
            resolve(result)
          }
        })
      }

      if (this.status === MyPromise.PENDING) {
        this.callbacks.push({
          onFulfilled: value => {
            setTimeout(() => {
              const result = onFulfilled(value)
               if (result instanceof MyPromise) {
                 result.then(resolve, reject)
               } else {
                resolve(result)
               }
            })
          },
          onRejected: value => {
            setTimeout(() => {
              const result = onRejected(value)
              if (result instanceof MyPromise) {
                result.then(resolve, reject)
              } else {
                resolve(result)
              }
            })
          }
        })
      }
    })
  }

验证

new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('成功')
  })
}).then(
  res => new MyPromise((resolve, reject) => {
    resolve(res)
  }),
  err => err
).then(res => console.log(res))

// 打印  成功

到这里 .then 方法就实现差不多了,接下来实现 catch 方法

catch

catch 方法可以处理拒绝的状态和错误的状态

  catch(onFulfilled) {
    if (typeof onFulfilled !== 'function') {
      onFulfilled = value => value
    }

    return new MyPromise((resolve, reject) => {
      if (this.status === MyPromise.REJECTED) {
        setTimeout(() => {
          const result = onFulfilled(this.value)
          if (result instanceof MyPromise) {
            result.then(resolve, reject)
          } else {
            resolve(result)
          }
        })
      }
    })
  }

验证

new MyPromise((resolve, reject) => {
  reject('失败')
}).catch(res=> console.log(res))

// 打印  失败

道理其实和 then 是相同的,到这里主功能基本上就差不多了,但是有很多重复的地方,优化一下

优化后完整代码


class MyPromise {
  // 准备三个状态
  static PENDING = 'pending';
  static FULFILLED = 'fulfilled';
  static REJECTED = 'rejected';

  constructor(executor) {
    this.status = MyPromise.PENDING; // 表示promise的状态
    this.value = null;  // 表示promise的值
    this.callbacks = [];
    try {
      executor(this.resolve.bind(this), this.reject.bind(this))
    } catch (error) {
      console.log(error)
      this.reject(error)
    }
  }
  resolve(value) {
    if (this.status === MyPromise.PENDING) {
      this.status = MyPromise.FULFILLED;
      this.value = value
    }
    this.callbacks.forEach(({ onFulfilled }) => onFulfilled(value))
  }
  reject(value) {
    if (this.status === MyPromise.PENDING) {
      this.status = MyPromise.REJECTED;
      this.value = value
    }
    this.callbacks.forEach(({ onRejected }) => onRejected(value))
  }
  parse({ callback, resolve, reject, value = this.value }) {
    setTimeout(() => {
      const result = callback(value)
      if (result instanceof MyPromise) {
        result.then(resolve, reject)
      } else {
        resolve(result)
      }
    })
  }
  then(onFulfilled, onRejected) {
    if (typeof onFulfilled !== 'function') {
      onFulfilled = value => value
    }

    if (typeof onFulfilled !== 'function') {
      onRejected = value => value
    }
    return new MyPromise((resolve, reject) => {
      if (this.status === MyPromise.FULFILLED) {
        this.parse({ callback: onFulfilled, resolve, reject })
      }

      if (this.status === MyPromise.REJECTED) {
        this.parse({ callback: onRejected, resolve, reject })
      }

      if (this.status === MyPromise.PENDING) {
        this.callbacks.push({
          onFulfilled: value => {
            this.parse({ callback: onFulfilled, resolve, reject, value })
          },
          onRejected: value => {
            this.parse({ callback: onRejected, resolve, reject, value })
          }
        })
      }
    })
  }
  catch(onFulfilled) {
    if (typeof onFulfilled !== 'function') {
      onFulfilled = value => value
    }

    return new MyPromise((resolve, reject) => {
      if (this.status === MyPromise.REJECTED) {
        this.parse({ callback: onFulfilled, resolve, reject })
      }
    })
  }
}

参考资料

后盾人