# 思考如何实现 Promise ?

  • Promise 类设计
    • 首先 Promise 有三种状态,如何进行状态之间的管理?
    • new Promise 的时候, 会传入一个函数 (executor),函数有 resolve 和 reject 两个参数。并且传入的函数是立即执行函数,如何实现?
    • 同时 resolve 和 reject 也会有参数, 所以我们如何管理参数?
  • then 方法实现思路
    • then 方法是所有方法里面考虑的情况最多的,也是较难实现的
    • then 调用必须在得到结果之后才执行,否则 then 方法会得到 undefined
    • then 方法需要获取到上一次的返回值,被进行传参。
    • then 方法是返回一个新的 Promise,除了抛出错误都会返回 resolve 函数
    • then 方法之处链式调用,也就是需要拿到每次执行结果的返回值
  • 另外还有一些方法就不列举了,下面都会进行实现

# Promise 结构设计

// 使用常量保存状态
const PROMISE_STATUS_PENDING = 'pending' 
const PROMISE_STATUS_FULFILLED = 'fulfilled' 
const PROMISE_STATUS_REJECTED = 'rejected' 
class _Promise {
  constructor(executor) {
    // 保存 Promise 状态
    this.status = PROMISE_STATUS_PENDING
    // 保存 Promise 参数
    this.value = null
    this.reason = null
    //resolve 函数
    const resolve = (value) => {
      // 判断状态 pending 状态执行 resolve
      if (this.status === PROMISE_STATUS_PENDING) {
        // 此时执行了这行代码代表调用了 resolve, 改变状态为 fulfilled
        this.status = PROMISE_STATUS_FULFILLED
        this.value = value
        console.log(this.value) // resolve
      }
    }
    //resolve 函数
    const reject = (reason) => {
      // 判断状态 pending 状态执行 resolve
      if (this.status === PROMISE_STATUS_PENDING) {
        // 此时执行了这行代码代表调用了 reject, 改变状态为 rejected
        this.status = PROMISE_STATUS_REJECTED
        this.reason = reason
        console.log(this.reason) 
      }
    }
    // 传入的回调函数
    executor(resolve, reject)
  }
}
// 测试:创建实例 p
const p = new _Promise((resolve, reject) => {
  resolve('resolve')
  reject('reject')
})

# then 方法问题与解决

# then 方法实现

// 使用常量保存状态
const PROMISE_STATUS_PENDING = 'pending' 
const PROMISE_STATUS_FULFILLED = 'fulfilled' 
const PROMISE_STATUS_REJECTED = 'rejected' 
class _Promise {
  constructor(executor) {
    // 保存 Promise 状态
    this.status = PROMISE_STATUS_PENDING
    // 保存 Promise 参数
    this.value = null
    this.reason = null
    //resolve 函数
    const resolve = (value) => {
      // 微任务
      queueMicrotask(() => {
          // 判断只有 pending 状态执行 resolve 回调函数
        if (this.status === PROMISE_STATUS_PENDING) {
          // 此时执行了这行代码代表调用了 resolve, 改变状态为 fulfilled
          this.status = PROMISE_STATUS_FULFILLED
          // 保存 value
          this.value = value
          console.log(this.value) // resolve
        }
      })
    }
    //reject 函数
    const reject = (reason) => {
      // 微任务 
      queueMicrotask(() => {
        // 判断只有 pending 状态执行 reject 回调函数
        if (this.status === PROMISE_STATUS_PENDING) {
          // 此时执行了这行代码代表调用了 reject, 改变状态为 rejected
          this.status = PROMISE_STATUS_REJECTED
          // reason
          this.reason = reason
          console.log(this.reason)
        }
      })
    }
    // 一旦 new _Promise 立即执行 executor 函数
    executor(resolve, reject)
  }
  //then 方法
  then(onFulfilled, onRejected) {
    this.onFulfilled = onFulfilled
    this.onRejected = onRejected
  }
}
// 测试:创建实例
const p = new _Promise((resolve, reject) => {
  resolve('resolve') // 只会执行 resolve
  reject('reject')
})
p.then(res => {
  console.log(res)
}, err => {
  console.log(err)
})
// 遇到的问题 1:同时调用多个 then 方法会把上面的 then 方法覆盖
p.then(res => {
  console.log(res)
}, err => {
  console.log(err)
})
// 遇到的问题 2:定时器在执行是状态就已经确定下来了 不会再进行回调
setTimeout(() => {
  p.then(res => {
    console.log(res)
  }, err => {
    console.log(err)
  })
}, 2000)

# then 遇到的问题

# 已解决

  1. 多个 then 方法调用会覆盖上面的 then 方法,只会得到最后一次的执行结果
  • 解决方法:定义两个数组分别存储成功的函数与失败的函数,每次调用都添加进对应的数组中,最后遍历调用
  1. 定时器在执行是状态就已经确定下来了 不会再进行回调
  • 解决方法:判断状态,如果状态已经为 onFulfilled 就立即调用,反之亦然
class _Promise {
  constructor(executor) {
    // 保存 Promise 状态
    this.status = PROMISE_STATUS_PENDING
    // 保存 Promise 参数
    this.value = null
    this.reason = null
    // 保存 then 成功与失败回调函数
    this.onFulfilledFns = []
    this.onRejectedFns = []
    //resolve 函数
    const resolve = (value) => {
      // 微任务
      queueMicrotask(() => {
        // 判断一下状态不为 pending 立即 return
        if(this.status !== PROMISE_STATUS_PENDING) return
          // 判断只有 pending 状态执行 resolve 回调函数
        if (this.status === PROMISE_STATUS_PENDING) {
          // 此时执行了这行代码代表调用了 resolve, 改变状态为 fulfilled
          this.status = PROMISE_STATUS_FULFILLED
          // 保存 value
          this.value = value
          // 对 then 成功的方法进行遍历调用
          this.onFulfilledFns.forEach(fn => {
            fn(this.value)
          })
        }
      })
    }
    //reject 函数
    const reject = (reason) => {
      // 微任务 
      queueMicrotask(() => {
        // 判断一下状态不为 pending 立即 return
        if(this.status !== PROMISE_STATUS_PENDING) return
        // 判断只有 pending 状态执行 reject 回调函数
        if (this.status === PROMISE_STATUS_PENDING) {
          // 此时执行了这行代码代表调用了 reject, 改变状态为 rejected
          this.status = PROMISE_STATUS_REJECTED
          // reason
          this.reason = reason
          // 对 then 失败的方法进行遍历调用
          this.onRejectedFns.forEach(fn => {
            fn(this.reason)
          })
        }
      })
    }
    // 一旦 new _Promise 立即执行 executor 函数
    executor(resolve, reject)
  }
  //then 方法
  then(onFulfilled, onRejected) {
    //if 判断状态 -> 解决定时器问题
    if (onFulfilled && this.status === PROMISE_STATUS_FULFILLED) {
      onFulfilled(this.value)
    }
    if (onRejected && this.status === PROMISE_STATUS_REJECTED) {
      onRejected(this.reason)
    }
    if (this.status === PROMISE_STATUS_PENDING) {
      this.onFulfilledFns.push(onFulfilled) 
      this.onRejectedFns.push(onRejected)
    }
  }
}
// 测试:创建实例
const p = new _Promise((resolve, reject) => {
  resolve('resolve') // 只会执行 resolve
  reject('reject')
})
p.then(res => {
  console.log(res) // resolve
}, err => {
  console.log(err)
})
// 遇到的问题 1:同时调用多个 then 方法会把上面的 then 方法覆盖 (已解决)
p.then(res => {
  console.log(res) // resolve
}, err => {
  console.log(err)
})
// 遇到的问题 2:定时器在执行是状态就已经确定下来了 不会再进行回调 (已解决)
setTimeout(() => {
  p.then(res => {
    console.log(res) // resolve
  }, err => {
    console.log(err)
  })
}, 2000)

# 未解决

  • then 方法的链式调用
  • then 方法每次返回新的 Promise, 与拿到返回值
  • then 方法未抛出错误都会执行 resolve

# then 方法解决

// 使用常量保存状态
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class _Promise {
  constructor(executor) {
    // 保存 Promise 状态
    this.status = PROMISE_STATUS_PENDING
    // 保存 Promise 参数
    this.value = null
    this.reason = null
    // 保存 then 成功与失败回调函数
    this.onFulfilledFns = []
    this.onRejectedFns = []
    //resolve 函数
    const resolve = (value) => {
      // 微任务
      queueMicrotask(() => {
        // 判断一下状态不为 pending 立即 return
        if(this.status !== PROMISE_STATUS_PENDING) return
          // 判断只有 pending 状态执行 resolve 回调函数
        if (this.status === PROMISE_STATUS_PENDING) {
          // 此时执行了这行代码代表调用了 resolve, 改变状态为 fulfilled
          this.status = PROMISE_STATUS_FULFILLED
          // 保存 value
          this.value = value
          // 对 then 方法进行遍历调用
          this.onFulfilledFns.forEach(fn => {
            fn(this.value)
          })
        }
      })
    }
    //reject 函数
    const reject = (reason) => {
      // 微任务
      queueMicrotask(() => {
        // 判断一下状态不为 pending 立即 return
        if(this.status !== PROMISE_STATUS_PENDING) return
        // 判断只有 pending 状态执行 reject 回调函数
        if (this.status === PROMISE_STATUS_PENDING) {
          // 此时执行了这行代码代表调用了 reject, 改变状态为 rejected
          this.status = PROMISE_STATUS_REJECTED
          // reason
          this.reason = reason
          this.onRejectedFns.forEach(fn => {
            fn(this.reason)
          })
        }
      })
    }
    // 一旦 new _Promise 立即执行 executor 函数
    try {
      executor(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }
  //then 方法
  then(onFulfilled, onRejected) {
    // 链式调用 返回新的 Promise
    return new _Promise((resolve, reject) => {
      //if 判断状态 -> 解决定时器问题
      if (onFulfilled && this.status === PROMISE_STATUS_FULFILLED) {
        try {
          const value = onFulfilled(this.value)
          resolve(value)
        } catch (err) {
          reject(err)
        }
      }
      if (onRejected && this.status === PROMISE_STATUS_REJECTED) {
        try {
          const reason = onRejected(this.reason)
          resolve(reason)
        } catch (err) {
          reject(err)
        }
      }
      if (this.status === PROMISE_STATUS_PENDING) {
        // 传入一个函数 在其内部进行得到返回值
        this.onFulfilledFns.push(() => {
          try {
            const value = onFulfilled(this.value)
            resolve(value)
          } catch (err) {
            reject(err)
          }
        })
        this.onRejectedFns.push(() => {
          try {
            const reason = onRejected(this.reason)
            resolve(reason)
          } catch (err) {
            reject(err)
          }
        })
      }
    })
  }
}
// 测试:创建实例
const p = new _Promise((resolve, reject) => {
  //resolve ('resolve') // 只会执行 resolve
  // reject('reject')
  throw new Error(233)
})
p.then(res => {
  console.log(res) // resolve
}, err => {
  console.log(err)
})
// 遇到的问题 1:同时调用多个 then 方法会把上面的 then 方法覆盖 (已解决)
p.then(res => {
  console.log(res) // resolve
}, err => {
  console.log(err)
})
// 遇到的问题 2:定时器在执行是状态就已经确定下来了 不会再进行回调 (已解决)
setTimeout(() => {
  p.then(res => {
    console.log(res) // resolve
  }, err => {
    console.log(err)
  })
}, 2000)

# then 方法优化

  • 我们可以看到这里面用了大量的 try catch,所以我会进行封装工具函数 execFunctionWithCatchError ()
// 使用常量保存状态
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
// 封装 tryCatch 工具函数
function execFunctionWithCatchError(execFunction, value, resolve, reject) {
  try {
    const result = execFunction(value)
    resolve(result)
  } catch (err) {
    reject(err)
  }
}
class _Promise {
  constructor(executor) {
    // 保存 Promise 状态
    this.status = PROMISE_STATUS_PENDING
    // 保存 Promise 参数
    this.value = null
    this.reason = null
    // 保存 then 成功与失败回调函数
    this.onFulfilledFns = []
    this.onRejectedFns = []
    //resolve 函数
    const resolve = (value) => {
      // 微任务
      queueMicrotask(() => {
        // 判断一下状态不为 pending 立即 return
        if(this.status !== PROMISE_STATUS_PENDING) return
          // 判断只有 pending 状态执行 resolve 回调函数
        if (this.status === PROMISE_STATUS_PENDING) {
          // 此时执行了这行代码代表调用了 resolve, 改变状态为 fulfilled
          this.status = PROMISE_STATUS_FULFILLED
          // 保存 value
          this.value = value
          // 对 then 方法进行遍历调用
          this.onFulfilledFns.forEach(fn => {
            fn(this.value)
          })
        }
      })
    }
    //reject 函数
    const reject = (reason) => {
      // 微任务
      queueMicrotask(() => {
        // 判断一下状态不为 pending 立即 return
        if(this.status !== PROMISE_STATUS_PENDING) return
        // 判断只有 pending 状态执行 reject 回调函数
        if (this.status === PROMISE_STATUS_PENDING) {
          // 此时执行了这行代码代表调用了 reject, 改变状态为 rejected
          this.status = PROMISE_STATUS_REJECTED
          // reason
          this.reason = reason
          this.onRejectedFns.forEach(fn => {
            fn(this.reason)
          })
        }
      })
    }
    // 在 new Promise 中,一旦抛出错误,就会执行 reject 函数
    try {
      executor(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }
  //then 方法
  then(onFulfilled, onRejected) {
    // 链式调用 返回新的 Promise
    return new _Promise((resolve, reject) => {
      //if 判断状态 -> 解决定时器问题
      if (onFulfilled && this.status === PROMISE_STATUS_FULFILLED) {
        execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
      }
      if (onRejected && this.status === PROMISE_STATUS_REJECTED) {
        execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
      }
      if (this.status === PROMISE_STATUS_PENDING) {
        // 传入一个函数 在其内部进行得到返回值
        this.onFulfilledFns.push(() => {
        execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
      })
       this.onRejectedFns.push(() => {
         execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
        })
      }
    })
  }
}
  • 可以看出代码量少了很多了

# catch 方法

  • catch 方法比较简单了,直接调用 then 方法就可以了
  • 对 push 方法优化严谨一些 catch 方法先判断下 onFulfilled & onRejected 有值的情况下再做 push 操作
  • catch 方法调的是 then 返回值的方法,是 new 的新_Promise,所以我们在 then 方法里面再加一句判断
    • onRejected ??= (err => { throw err })
class _Promise {
  ......
  ......
  //then 方法
  then(onFulfilled, onRejected) {
    // 判断下是否有有值  没有就抛出
    onRejected ??= (err => { throw err })
    
    // 链式调用 返回新的 Promise
    return new _Promise((resolve, reject) => {
      //if 判断状态 -> 解决定时器问题
      if (onFulfilled && this.status === PROMISE_STATUS_FULFILLED)
        execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
      
      if (onRejected && this.status === PROMISE_STATUS_REJECTED)
        execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
      
      if (this.status === PROMISE_STATUS_PENDING) {
        // 严谨点 先判断是否有值 传入一个函数 在其内部进行得到返回值
        if(onFulfilled) this.onFulfilledFns.push(() => 
        execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
      )
        if(onRejected) this.onRejectedFns.push(() => 
          execFunctionWithCatchError(onRejected, this.reason, resolve, reject))
      }
    })
  }
  //catch 方法
  catch(onRejected) {
    //return 出 catch 的返回值
    return this.then(null, onRejected)
  }
}
// 测试代码:
new _Promise((resolve, reject) => {
  throw new Error('我被抛出了/(ㄒoㄒ)/~~')
})
.catch(err => {
  console.log(err) // Error: 我被抛出了 /(ㄒ o ㄒ)/~~
})
new _Promise((resolve, reject) => {
  throw new Error('我被抛出了/(ㄒoㄒ)/~~')
})
.then(null, err => {
  console.log(err) // Error: 我被抛出了 /(ㄒ o ㄒ)/~~
})
.catch(err => {
  // 因为上面已经用 err 接收了
  console.log(err) 
})
  • 补充一点:如果不加这一句,类似下面这种 .then(res => console.log(res)) , 这种没有 err 参数时,then 就没有 err 函数就是 undefined,那么执行时 then 方法时, onRejectedundefined ,所以会报 execFn is not a function ,这是只要判断一下,在为 undefined 时抛出一个函数即可
const p = new _Promise((resolve, reject) => {
  throw new Error(2333)
})
p.then(res => {
  throw new Error(1123)
})
.catch(err=>{
  console.log(err);
})

# finally 方法

  • finally 不管是成功还是失败,都会调用 finally
  • finally 调用的也是 then,但 resolve 方法断层了,需要酱结果返回出去
const p = new _Promise((resolve, reject) => {
  resolve('resolve')
})
.then(res => {
  console.log('res1', res) // res1 resolve
  return null
})
.catch(err => console.log('err', err))
.finally((res) => console.log(res, 'finally')) // TypeError: execFn is not a function ...
  • 如果不加上 onFulfilled ??= (value => value) 这句代码,就会断层出现上面的问题
class _Promise {
  constructor(executor) {
	......
    ......
  }
  //then 方法
  then(onFulfilled, onRejected) {
    // 判断下是否有有值  没有就抛出
    onRejected ??= (err => { throw err })
    // 如果没有值 添加一个回调 不处理的话把值返回出去 给下一个有 resolve 值来执行
    onFulfilled ??= (value => value)
    // 链式调用 返回新的 Promise
    return new _Promise((resolve, reject) => {
      //if 判断状态 -> 解决定时器问题   多余的是否为空的判断经过上面的判断可以删掉了
      if (this.status === PROMISE_STATUS_FULFILLED) {
        execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
      }
      if (this.status === PROMISE_STATUS_REJECTED) {
        execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
      }
      if (this.status === PROMISE_STATUS_PENDING) {
        // 传入一个函数 在其内部进行得到返回值
        this.onFulfilledFns.push(() => {
        execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
      })
          
          this.onRejectedFns.push(() => {
          execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
        })
      }
    })
  }
  //catch 方法
  catch(onRejected) {
    return this.then(null, onRejected)
  }
  //finally 方法
  finally(onFinally) {
    this.then(onFinally, onFinally)
  }
}
// 测试代码:
new _Promise((resolve, reject) => {
  resolve('resolve 2333')
})
.then(res => {
  console.log(res) // resolve 2333
  return 'aaaa'
})
.catch(err => {
    console.log(err)
})
.finally(() => {
  console.log('finally') // finally
})

# static resolve

  • 这个方法就很简单了,返回_Promise 就 OK 了
//resolve 方法
  static resolve(value) {
    return new _Promise(resolve => resolve(value))
  }

# static reject

  • 这个方法也很简单了, new _Promise((resolve, reject) => reject(reason) 搞定
//reject 方法
  static reject(reason) {
    return new _Promise((resolve, reject) => reject(reason))
  }

# static all

  • 什么时候调 resolve?全部都有结果了才会调用,那么可以创建一个空数组 (values),用来收集结果。

  • 判断一下 values === promise,如果长度一致那么说明已经全部执行完并保存到数组中了

  • all 方法一旦有一个 reject 那么就会调用 reject

static all(promises) {
  const values = []
  return new _Promise((resolve, reject) => {
    promises.forEach((promise, index) => {
      promise.then(res => {
        queueMicrotask(() => values[index] = res)
        if (values.length === promises.length) resolve(values)
      }, err => {
          console.error('Uncaught (in promise)', err);
          // reject(err)
      })
    })
  })
}

# static allSettled

  • allSettled 方法也比较简单,先定义一个空数组,然后对 allSettled 所有的参数进行遍历,无论成功还是失败都进行 push
  • push 会返回数组,数组里面则是一个个对象,对象里保存了状态与结果
  • 然后判断成功或失败的长度,一旦其中 res 或 err 里面哪个长度与参数长度一样,就代表全部执行完并全部 push,这时候就直接只是 resolve 就行了 (无论是成功还是失败都执行)
static allSettled(promises) {
  const results = []
  return new _Promise((resolve, reject) => {
    promises.forEach((promise, index) => {
      promise.then(res => {
        queueMicrotask(() => results[index] = { status: PROMISE_STATUS_FULFILLED, value: res })
        if(results.length === promises.length) resolve(results)
      }, err => {
        queueMicrotask(() => results[index] = { status: PROMISE_STATUS_REJECTED, value: err })
        if(results.length === promises.length) reject(results)
      })
    })
  })
}

# static race

  • race (竞赛),一旦有结果立马返回 resolve 现有结果就执行 resolve,reject 现有结果就执行 reject。
  • 核心代码 promise.then(res => resolve(res), err => reject(err))
//race 方法
  static race(promises) {
    return new _Promise((resolve, reject) => {
      promises.forEach(promise => {
      //promise.then (res => resolve (res), err => reject (err)) 优化代码 👇
        promise.then(resolve,reject )
      })
    })
  }

# static any

  • any 方法一旦有成功的结果就会返回,但是如果失败了就会接着往下执行,直到全部执行完。
  • 但如果全部失败,就会返回一个合集我们就可以使用 new AggregateError 酱 reason 传入就可以了
  • 首先我们创建一个空数组,进行遍历,按照上面的分析与逻辑进行代码执行就行了。
  • 但如果失败就会 push 进数组,直到全部失败。那么数组长度也就会与传进来的数组长度一致,就进行 reject(new AggregateError(reasons))
static any(promises) {
  const reasons = [] // 保存所有失败结果
  const indexs = []  // 保存 Promise 执行的顺序
  const results = [] // 返回的最终数组
  return new _Promise((resolve, reject) => {
    promises.forEach((promise, index) => {
      promise.then(resolve).catch(err => {
        reasons.push(err)
        indexs.push(index)
        if (reasons.length === promises.length) {
          for (let i = 0; i < indexs.length; i++) results[indexs[i]] = reasons[i]
          reject(new AggregateError(results, 'All promises were rejected'))
        }
      })
    })
  })
}

# 测试

const p1 = new _Promise((resolve, reject) => setTimeout(() => reject(1), 1000))
const p2 = new _Promise((resolve, reject) => setTimeout(() => reject(2), 5000))
const p3 = new _Promise((resolve, reject) => setTimeout(() => reject(3), 3000))
_Promise.any([p1,p2,p3]).then(res => {console.log(res)}, err => console.log(err.errors))

# static randomStatePromise

  • 这个静态方法主要是为 retry 准备的 就是返回一个随机状态的 Promise
// 随机状态 Promise
static randomStatePromise (n = 3, m = 10){
    const result = ~~(Math.random() * m)
    return new _Promise((resolve, reject) => {
      return result < n ? resolve({status: 'fulfilled', value: result}) : reject({status: 'rejected', value: result})
    })
  }

# static retry

  • 这个随机状态的 Promise 比较简单
  • 难点在于利用 setTimeout 进行递归调用
// 传入函数 请求次数 延迟时间
static retry(fn = _Promise.randomStatePromise, times = 5, delay = 1000) {
  return new _Promise((resolve, reject) => {
    const tFn = () => {
      fn().then(resolve).catch(err => {
        if (times-- > 0) {
          setTimeout(tFn, delay)
          console.log(`还剩${times}次机会`, err);
        }
      })
    }
    return tFn()
  })
}
  • 测试代码
// 测试
_Promise.retry(randomStatePromise)
.then(res => {
  console.log(res)
}, err => {
  console.log(err)
})
  • 嗯~这大概就是我的整个手写 Promise 了!~