【JS】手写简易Promise

Promise 是 JS 中处理异步的一种方式,传统方式通过 callbacks,但是会有回调地狱问题,可读性极差。

Promise 仍然存在链式调用的问题,然后出现了 async await 语法糖,这两个语法糖通过 Promise+Generator 实现。

Promise A+ 规范
Promise 在整个生命周期中有且仅有三种状态,一旦状态变为 FulfilledRejected,Promise 的状态将变为不可变

  • Pending(进行中):Promise 初始状态,可以转变为 Fulfilled 或 Rejected。
  • Fulfilled(已成功):Promise 完成状态,表示操作成功完成,并有一个值。完成状态不可更改。
  • Rejected(已失败):Promise 拒绝状态,表示操作失败,并有一个拒绝原因。拒绝状态不可更改。

手写一个简单的 Promise

const PENDING = "pending"
const RESOLVED = "resolved"
const REJECTED = "rejected"

class MyPromise {
  constructor(executor) {
    this.status = PEDING
    this.value = null
    this.reason = null

    const resolve = (value) => {
      this.status = RESOLVED
      this.value = value
    }
    const reject = (reason) => {
      this.status = REJECTED
      this.reason = reason
    }

    try {
      executor(resolve, reject)
    } catch (e) {
      reject(e)
    }
  }

  then(onFulfilled, onRejected) {
    if (this.status === FULFILLED) {
      onFulfilled(this.value)
    } else if (this.status === REJECTED) {
      onRejected(this.reason)
    }
  }
}

考虑异步情况,then时判断如果状态仍为 PENDING 则追加到回调列表,不能确保1秒后才执行then函数,但是可以保证1秒后再执行then里的回调。

const PENDING = "pending"
const RESOLVED = "resolved"
const REJECTED = "rejected"

class MyPromise {
  constructor(executor) {
    this.status = PENDING
    this.value = null
    this.reason = null
    this.fulfilledCallbacks = [] // 存储then成功的回调
    this.rejectedCallbacks = [] // 存储then失败的回调

    const resolve = (value) => {
      if (this.status === PENDING) {
        this.value = value
        this.status = RESOLVED
        this.fulfilledCallbacks.forEach((cb) => cb(value))
      }
    }
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason
        this.status = REJECTED
        this.rejectedCallbacks.forEach((cb) => cb(reason))
      }
    }
    try {
      executor(resolve, reject)
    } catch (e) {
      reject(e)
    }
  }
  
  then(onFulfilled, onRejected) {
    if (this.status === RESOLVED) {
      onFulfilled(this.value)
    } else if (this.status === REJECTED) {
      onRejected(this.reason)
    } else if (this.status === PENDING) {
      this.fulfilledCallbacks.push(() => {
        onFulfilled(this.value)
      })
      this.rejectedCallbacks.push((reason) => {
        onRejected(reason)
      })
    }
  }
  
  catch(onRejected) {
    if (this.status === REJECTED) {
      onRejected(this.reason)
    } else if (this.status === PENDING) {
      this.rejectedCallbacks.push((reason) => {
        onRejected(reason)
      })
    }
  }
}

MyPromise.all = function (promises) {
  return new MyPromise((resolve, reject) => {
    let count = 0
    const result = []
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(
        (value) => {
          count++
          result[i] = value
          if (count === promises.length) {
            resolve(result)
          }
        },
        (reason) => {
          reject(reason)
        }
      )
    }
  })
}

MyPromise.race = function (promises) {
  return new MyPromise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(
        (value) => {
          resolve(value)
        },
        (reason) => {
          reject(reason)
        }
      )
    }
  })
}

测试

// 测试同步resolve
const promise1 = new MyPromise((resolve) => {
  resolve("同步成功")
})
promise1.then(
  (value) => console.log("同步测试1:", value), // 期望输出: "同步测试1: 同步成功"
  (reason) => console.log("同步测试1失败:", reason)
)

// 测试同步reject
const promise2 = new MyPromise((_, reject) => {
  reject("同步失败")
})
promise2.then(
  (value) => console.log("同步测试2:", value),
  (reason) => console.log("同步测试2失败:", reason) // 期望输出: "同步测试2失败: 同步失败"
)

// 测试异步resolve
const promise3 = new MyPromise((resolve) => {
  setTimeout(() => resolve("异步成功"), 100)
})
promise3.then(
  (value) => console.log("异步测试1:", value), // 期望输出: "异步测试1: 异步成功"
  (reason) => console.log("异步测试1失败:", reason)
)

// 测试异步reject
const promise4 = new MyPromise((_, reject) => {
  setTimeout(() => reject("异步失败"), 100)
})
promise4.then(
  (value) => console.log("异步测试2:", value),
  (reason) => console.log("异步测试2失败:", reason) // 期望输出: "异步测试2失败: 异步失败"
)

// 测试 catch 异步错误
const promise5 = new MyPromise((_, reject) => {
  setTimeout(() => reject("catch测试异步失败"), 100)
})
promise5.catch((reason) => console.log("catch测试:", reason)) // 期望输出: "catch测试: catch测试异步失败"

// 测试 MyPromise.all,全部成功
const promise6 = new MyPromise((resolve) => resolve("结果1"))
const promise7 = new MyPromise((resolve) => resolve("结果2"))
MyPromise.all([promise6, promise7]).then(
  (values) => console.log("Promise.all成功:", values), // 期望输出: "Promise.all成功: [ '结果1', '结果2' ]"
  (reason) => console.log("Promise.all失败:", reason)
)

// 测试 MyPromise.all,部分失败
const promise8 = new MyPromise((resolve) => resolve("结果1"))
const promise9 = new MyPromise((_, reject) => reject("失败原因"))
MyPromise.all([promise8, promise9]).then(
  (values) => console.log("Promise.all成功:", values),
  (reason) => console.log("Promise.all失败:", reason) // 期望输出: "Promise.all失败: 失败原因"
)

// 测试 MyPromise.race,返回最先完成的结果
const promise10 = new MyPromise((resolve) =>
  setTimeout(() => resolve("最先完成"), 50)
)
const promise11 = new MyPromise((resolve) =>
  setTimeout(() => resolve("较晚完成"), 100)
)
MyPromise.race([promise10, promise11]).then(
  (value) => console.log("Promise.race成功:", value), // 期望输出: "Promise.race成功: 最先完成"
  (reason) => console.log("Promise.race失败:", reason)
)

// 测试 MyPromise.race,返回最先失败的结果
const promise12 = new MyPromise((_, reject) =>
  setTimeout(() => reject("最先失败"), 50)
)
const promise13 = new MyPromise((resolve) =>
  setTimeout(() => resolve("成功结果"), 100)
)
MyPromise.race([promise12, promise13]).then(
  (value) => console.log("Promise.race成功:", value),
  (reason) => console.log("Promise.race失败:", reason) // 期望输出: "Promise.race失败: 最先失败"
)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

田本初

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值