Promise 系列

规律:都是 return new Promise((resolve, reject) => { ... }) 作为出口

1 Promise.all

成功用数组计数,失败直接 reject
// 成功了就用数组计数,失败了就直接 reject
Promise.myAll = function (promiseArr) {
  if (promiseArr.length === 0) return Promise.resolve([])
  let ans = [], count = 0
  return new Promise((resolve, reject) => {
    for (let i = 0; i < promiseArr.length; i++) {
      Promise.resolve(promiseArr[i]).then(res => {
        ans[i] = res // 用 i 而不是 push,保证顺序和传入一致
        count++
        if (count === promiseArr.length) resolve(ans) // 全部成功才 resolve
      }).catch(reject) // 任一失败直接 reject
    }
  })
}

2 Promise.race

返回最快的那个(成功或失败)
// 谁先 settle 就用谁的结果,后面的自动忽略(Promise 只能 resolve/reject 一次)
Promise.myRace = function (arr) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < arr.length; i++) {
      Promise.resolve(arr[i]).then(resolve, reject)
    }
  })
}

3 Promise.any

和 all 相反:成功直接 resolve,失败用数组计数
// 和 all 相反:成功了就直接 resolve,失败了才用数组计数
Promise.myAny = function (arr) {
  let count = 0
  const errors = []
  return new Promise((resolve, reject) => {
    if (arr.length === 0) {
      reject(new AggregateError([], 'All promises were rejected'))
      return
    }
    for (let i = 0; i < arr.length; i++) {
      Promise.resolve(arr[i]).then(resolve).catch(err => { // 任一成功直接 resolve
        errors[i] = err
        count++
        if (count === arr.length) { // 全部失败才 reject
          reject(new AggregateError(errors, 'All promises were rejected'))
        }
      })
    }
  })
}

4 Promise.allSettled

成功或失败 count 都 +1,最后 resolve 出来
  • :返回的是 { status, value },而 all 返回的只是 value
// 成功或失败 count 都 +1,最后全部 resolve 出来
// 坑点:返回的是 { status, value/reason },而 all 返回的只是 value
Promise.myAllSettled = function (promiseArr) {
  if (promiseArr.length === 0) return Promise.resolve([])
  let ans = [], count = 0
  return new Promise((resolve) => {
    for (let i = 0; i < promiseArr.length; i++) {
      Promise.resolve(promiseArr[i]).then(res => {
        ans[i] = { status: 'fulfilled', value: res }
      }).catch(err => {
        ans[i] = { status: 'rejected', reason: err }
      }).finally(() => {
        count++ // 不管成功失败都计数
        if (count === promiseArr.length) resolve(ans) // 永远 resolve,不会 reject
      })
    }
  })
}

5 超时重试 fetchWithRetry

AbortController + for 循环重试(工程正确版)

题干:实现 fetchWithRetry(url, timeout, retries),要求:请求超时后自动取消并重试,最多重试 retries 次,全部失败则抛出错误。

  • AbortController 取消超时请求
  • 注意用 await 不要用 .then,否则 try/catch 捕获不到
async function fetchWithRetry(url, timeout = 5000, retries = 3) {
  for (let attempt = 0; attempt <= retries; attempt++) {
    const controller = new AbortController()
    const timer = setTimeout(() => controller.abort(), timeout) // 超时自动 abort

    try {
      // 注意这里要 await 不要 .then,try/catch 捕获不到 .then 里的错误
      const res = await fetch(url, { signal: controller.signal })
      if (!res.ok) throw new Error(`HTTP ${res.status}`)
      return res // 成功直接返回
    } catch (err) {
      if (attempt === retries) throw err // 最后一次重试还失败就抛出
      // 否则继续下一轮 for 循环重试
    } finally {
      clearTimeout(timer) // 无论成功失败都清除定时器
    }
  }
}

6 并发控制 Scheduler

先跑满 limit 个,每跑完一个拉下一个

题干:实现一个带并发数限制的异步任务调度器 Scheduler(limit)。同一时刻最多有 limit 个任务在执行,当某个任务完成后自动拉取队列中的下一个任务。

class Scheduler {
  constructor(limit) {
    this.limit = limit
    this.queue = []
  }

  add(promiseCreator) {
    this.queue.push(promiseCreator)
  }

  start() {
    // 每次跑完一个就拉下一个(finally 保证成功失败都会拉)
    const runOne = async () => {
      if (!this.queue.length) return
      const fn = this.queue.shift()
      try {
        await fn()
      } finally {
        runOne() // 跑完拉一个
      }
    }

    // 先跑满 limit 个
    for (let i = 0; i < this.limit; i++) {
      runOne()
    }
  }
}

// 测试
const timeout = (time) => new Promise(r => setTimeout(r, time))
const scheduler = new Scheduler(2)
const addTask = (time, order) => {
  scheduler.add(() => timeout(time).then(() => console.log(order)))
}
addTask(1000, '1'); addTask(500, '2')
addTask(300, '3');  addTask(400, '4')
scheduler.start()
// 输出: 2 → 3 → 1 → 4
带返回值版(return new Promise 作为出口)
// 带返回值版:用 return new Promise 作为出口,收集全部结果
function myFetch(tasks, limit) {
  let curCount = 0, curIndex = 0, finishCount = 0
  const results = []

  return new Promise((resolve) => {
    const getNext = () => {
      if (curCount >= limit || curIndex >= tasks.length) return
      const idx = curIndex++ // 用 idx 保存当前索引,保证结果顺序
      curCount++
      tasks[idx]().then(res => {
        results[idx] = res
      }).catch(err => {
        results[idx] = false
      }).finally(() => {
        finishCount++; curCount--
        getNext() // 跑完一个拉下一个
        if (finishCount === tasks.length) resolve(results) // 全部完成
      })
    }
    // 先跑满 limit 个
    while (curCount < limit && curIndex < tasks.length) getNext()
  })
}

7 实现 Promise

SimplePromise(支持链式调用 + 异步)
  • 两个关键点:① resolve 改变状态 ② then 注册回调
  • pending 时存回调,settled 后用 setTimeout 异步执行
class SimplePromise {
  constructor(executor) {
    this.state = 'pending'
    this.value = null
    this.callbacks = [] // pending 时存回调,settled 后执行

    const resolve = (value) => this.transition('fulfilled', value)
    const reject = (reason) => this.transition('rejected', reason)

    try { executor(resolve, reject) }
    catch (error) { reject(error) } // executor 里 throw 的错误也要 reject
  }

  transition(state, value) {
    if (this.state !== 'pending') return // 状态不可变,只能从 pending 转一次
    this.state = state
    this.value = value
    // 用 setTimeout 模拟微任务,确保 then 注册的回调在当前同步代码之后执行
    setTimeout(() => {
      this.callbacks.forEach(cb => this.handle(cb))
    }, 0)
  }

  handle(callback) {
    const { onFulfilled, onRejected, resolve, reject } = callback
    try {
      if (this.state === 'fulfilled') {
        this.executeCallback(onFulfilled, resolve, reject)
      } else if (this.state === 'rejected') {
        this.executeCallback(onRejected, resolve, reject)
      }
    } catch (error) { reject(error) }
  }

  executeCallback(callback, resolve, reject) {
    try {
      if (typeof callback === 'function') {
        resolve(callback(this.value))
      } else {
        resolve(this.value)
      }
    } catch (error) { reject(error) }
  }

  then(onFulfilled, onRejected) {
    return new SimplePromise((resolve, reject) => {
      const callback = { onFulfilled, onRejected, resolve, reject }
      if (this.state === 'pending') {
        this.callbacks.push(callback)
      } else {
        setTimeout(() => this.handle(callback), 0)
      }
    })
  }
}