手写 Promise, 只要认真看, 相信你一定看得懂(一)

1,983 阅读7分钟

前言

手写 Promise 听说面试常考, 反正我现在没有遇到过,为了以防万一,先准备准备.

Promise 核心逻辑实现

我们先简单使用一下 promise ,假设你们都已经了解 promise 的 api 如何使用

let promise = new Promise((resolve, reject) => {
  resolve('成功')
  // reject('失败')
})
promise.then(value => {
  console.log(value)  // 成功
}, reason => {
  console.log(reason)
})

初始化 MyPromise

Promise 是一个类,通过new 关键字实例化Promise,并传入了一个执行器函数,该函数接受两个参数,两个参数也分别都是函数,当其中一个执行之后状态就不会再改变. 这里引出 Promise有三种状态, pending:初始化状态, fullfilled;成功状态, rejected: 失败状态.状态只能从pending -> fullfilled 或者 pending -> rejected,一旦变为 fullfilled或者 rejected就不会再改变了.那么什么时候改变状态呢?接下来我们来实现一下源码

class MyPromise {
  constructor(executor) {
    // new Mypromise会立即执行 executor
    // executor 中有两个方法分别是 resolve, reject
    executor(this.resolve, this.reject)
  }
  resolve = () => {

  }
  reject = () => {

  }
}

为 Mypromise 添加状态

先从简单开始, 我们要实现一个自己的 MyPromise, new 的时候传递了一个函数,并且是立即执行的,所以我们在 constructor传入 executor 方法,并立即执行它.并且 resolve和reject是 executor 中的两个参数,分别对应两个方法.我们接着往下敲

const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败
class MyPromise {
  // 默认为等待状态
  status = PENDING
   constructor(executor) {
   ...
  }
  resolve = () => {
    // 不是pending状态说明 状态已经改变,就不会往下执行了
    if(this.status!== PENDING) return;
    // 执行 resolve时修改状态
    this.status = FULFILLED
  }
  reject = () => {
     // 不是pending状态说明 状态已经改变,就不会往下执行了
    if(this.status !== PENDING) return;
    // 执行 reject时修改状态
    this.status = REJECTED;
  }
}

为 MyPromise 添加 then 方法

这里我们增加了状态的概念, 首先定义了三个状态, 在类里面初始化一个状态status为pending状态,执行 resolve前判断状态是否改变,改变了就不执行,否则改变状态, reject类似.接着往下

class MyPromise {
  // 默认为等待状态
  status = PENDING
  value = undefined
  reason = undefined
  constructor(executor) {
   ...
  }
  resolve = value => {
   ...
    this.value = value
  }
  reject = reason => {
     ...
    this.reason = reason
  }
  then(successCallback, failCallback) {
  // 新增两个回调,当状态为 fullfilled,说明调用了 resolve,代表成功回调,调用 successCallback
    // 我们使用 then的时候会接受到一个值,这个值是在 resolve执行之后传递过来的,failCallbac
    if(this.status === FULFILLED) successCallback(this.value)
    else if(this.status === REJECTED) failCallback(this.value)
  }
}

这里我们增加了 then方法,调用 then的时候 then(value => console.log(value))我们可以直接获取到这个 value的值,那么这个值那里来的呢? 这个值是在 promise中调用了 resolve('成功')传递过来的,所以我在 MyPromise中定义了一个 value, 在 resolve执行的时候将 value赋值给 this.value,然后 successCallback就可以拿到值了, 这个时候我们执行一下

let promise = new MyPromise((resolve, reject) => {
  resolve('成功')
  // reject('失败')
})
promise.then(value => {
  console.log(value)  // 成功
}, reason => {
  console.log(reason) 
})

就会发现 已经能够打印出成功,至此, promise核心逻辑完了,是不是觉得挺 easy 😎.
上面的完整代码

详细代码点击我展开 👈
const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败
class MyPromise {
  // 默认为等待状态
  status = PENDING
  value = undefined
  reason = undefined
  constructor(executor) {
    // new Mypromise会立即执行 executor
    // executor 中有两个方法分别是 resolve, reject
    executor(this.resolve, this.reject)
  }
  resolve = value => {
    // 不是pending状态说明 状态已经改变,就不会往下执行了
    if(this.status!== PENDING) return;
    // 执行 resolve 时修改状态
    this.status = FULFILLED;
    this.value = value
  }
  reject = reason => {
     // 不是pending状态说明 状态已经改变,就不会往下执行了
    if(this.status !== PENDING) return;
    // 执行 reject 时修改状态
    this.status = REJECTED;
    this.reason = reason
  }
  then(successCallback, failCallback) {
    // 新增两个回调,当状态为 fullfilled,说明调用了 resolve,代表成功回调,调用 successCallback
    // 我们使用 then的时候会接受到一个值,这个值是在 resolve执行之后传递过来的,failCallback类似
    if(this.status === FULFILLED) successCallback(this.value)
    else if(this.status === REJECTED) failCallback(this.reason)
  }
}

添加异步逻辑

先看示例代码

let promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
  resolve('成功')
}, 200);  
})
promise.then(value => {
  console.log(value)  // 什么也没有打印
}, reason => {
  console.log(reason) 
})

当我们给 resolve 加上一个异步时就会发现 console 什么也没有打印,这是什么原因呢?

 if(this.status === FULFILLED) successCallback(this.value)
    else if(this.status === REJECTED) failCallback(this.value)

new Promise 这个动作执行完成之后就会立即执行 promise.then,而这个时候 状态其实还是pending,而又因为我们在 then 源码里面加了判断条件状态为 fullfilled 或者 rejected 时才会执行回调,所以现在什么也没有执行.
接下来我们就来实现异步的操作

class MyPromise {
  ...
  successCallback = undefined
  failCallback = undefined
  constructor(executor) {
    ...
  }
  resolve = value => {
    ...
    this.successCallback && this.successCallback(value)
  }
  reject = reason => {
    ...
    this.failCallback && this.failCallback(reason)
  }
  then(successCallback, failCallback) {
    ...
    else {
      // 当 status 为pending时 保存 successCallback和 failCallback,当要执行 resolve时,我们再到resolve执行successCallback
      this.successCallback = successCallback
      this.failCallback = failCallback
    }
  }
}

首先我们定义了两个回调函数
我们在 then 里面增加一个 else 语句用来判断当状态为 pending 时, 我们先保存两个回调函数 当异步执行 resolve 的时候我们去调用回调函数.所以在 resolve中加了一行代码,首先判断this.successCallback是否有,没有代表 resolve没有异步操作,也就不会执行,有就会执行并把 value穿给回调, 失败回调类似,就不赘述了.

let promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
  resolve('成功')
}, 200);  
})
promise.then(value => {
  console.log(value)  // 成功
}, reason => {
  console.log(reason) 
})

然后我们再打印就会发现可以正常输出啦
上述完整代码如下

详细代码点击我展开 👈
const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败
class MyPromise {
  // 默认为等待状态
  status = PENDING
  value = undefined
  reason = undefined
  constructor(executor) {
    // new Mypromise会立即执行 executor
    // executor 中有两个方法分别是 resolve, reject
    executor(this.resolve, this.reject)
  }
  resolve = value => {
    // 不是pending状态说明 状态已经改变,就不会往下执行了
    if(this.status!== PENDING) return;
    // 执行 resolve 时修改状态
    this.status = FULFILLED;
    this.value = value
  }
  reject = reason => {
     // 不是pending状态说明 状态已经改变,就不会往下执行了
    if(this.status !== PENDING) return;
    // 执行 reject 时修改状态
    this.status = REJECTED;
    this.reason = reason
  }
  then(successCallback, failCallback) {
    // 新增两个回调,当状态为 fullfilled,说明调用了 resolve,代表成功回调,调用 successCallback
    // 我们使用 then的时候会接受到一个值,这个值是在 resolve执行之后传递过来的,failCallback类似
    if(this.status === FULFILLED) successCallback(this.value)
    else if(this.status === REJECTED) failCallback(this.reason)
  }
}

实现then方法多次调用(不是链式调用)

再看一个示例

let promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
  resolve('成功')
}, 200);  
})
promise.then(value => {
  console.log(value)  // 什么也没有输出
}, reason => {
  console.log(reason) 
})
promise.then(value => {
  console.log(value)  // 输出成功
}, reason => {
  console.log(reason) 
})

这里解释起来稍微有那么一丢丢复杂,慢慢来,首先第一次执行 promise.then 时 resolve 还未执行,这个时候 其实 代码会接着往下执行,也就是执行下一个promise.then
源码中第一次执行 promise.then的时候做了什么事呢? 就是将 successcallback 保存了起来,那么其实就会把上一次的 successcallback 覆盖掉了. 只会执行最后一个 then 了,不知道这么说有没有看懂呢???
所以接下来我们就来解决这个问题,上代码

class MyPromise {
  // successCallback = undefined
  // failCallback = undefined
  // 修改 successCallback 和 failCallback 为数组
  successCallback = []
  failCallback = []
  constructor(executor) {
   ...
  }
  resolve = value => {
    ...
    // this.successCallback && this.successCallback(value)
    while(this.successCallback.length) this.successCallback.shift()(value)
  }
  reject = reason => {
    ...
    // this.failCallback && this.failCallback(reason)
    while(this.failCallback.length) this.failCallback.shift()(reason)
  }
  then(successCallback, failCallback) {
    ...
    else {
      // 当 status 为pending时 保存 successCallback和 failCallback,当要执行 resolve时,我们再到resolve执行successCallback
      // this.successCallback = successCallback
      // this.failCallback = failCallback
      // 将一个个的callback推入数组
      this.successCallback.push(successCallback)
      this.failCallback.push(failCallback)
    }
  }
}

简单来说就是 将 回调函数 通过数组的形式保存, 每次通过 原对象.then 都会push一个回调(注意只有异步resolve才会push,可以思考下为什么同步就不需要push呢) 这个时候再执行上面的案例就发现两个promise都成功执行了

image.png

详细代码点击我展开 👈
const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败
class MyPromise {
  // 默认为等待状态
  status = PENDING
  value = undefined
  reason = undefined
  // successCallback = undefined
  // failCallback = undefined
   // 修改 successCallback 和 failCallback 为数组
  successCallback = []
  failCallback = []
  constructor(executor) {
    // new Mypromise会立即执行 executor
    // executor 中有两个方法分别是 resolve, reject
    executor(this.resolve, this.reject)
  }
  resolve = value => {
    // 不是pending状态说明 状态已经改变,就不会往下执行了
    if(this.status!== PENDING) return;
    // 执行 resolve 时修改状态
    this.status = FULFILLED;
    this.value = value
    // this.successCallback && this.successCallback(value)
    while(this.successCallback.length) this.successCallback.shift()(value)
  }
  reject = reason => {
     // 不是pending状态说明 状态已经改变,就不会往下执行了
    if(this.status !== PENDING) return;
    // 执行 reject 时修改状态
    this.status = REJECTED;
    this.reason = reason
    // this.failCallback && this.failCallback(reason)
    while(this.failCallback.length) this.failCallback.shift()(reason)
  }
  then(successCallback, failCallback) {
    // 新增两个回调,当状态为 fullfilled,说明调用了 resolve,代表成功回调,调用 successCallback
    // 我们使用 then的时候会接受到一个值,这个值是在 resolve执行之后传递过来的,failCallback类似
   
    if(this.status === FULFILLED) successCallback(this.value)
    else if(this.status === REJECTED) failCallback(this.reason)
    else {
      // 当 status 为pending时 保存 successCallback和 failCallback,当要执行 resolve时,我们再到resolve执行successCallback
      // this.successCallback = successCallback
      // this.failCallback = failCallback
      this.successCallback.push(successCallback)
      this.failCallback.push(failCallback)
    }
  }
}
let promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
  resolve('成功')

}, 200);})
promise.then(value => {
  console.log(value)  // 成功
}, reason => {
  console.log(reason) 
})
promise.then(value => {
  console.log(value)  // 成功
}, reason => {
  console.log(reason) 
})

实现then的链式调用

现在文章篇幅过长,放到下一篇, 这里在我看来是实现promise最难的地方