Promise 系列
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)
}
})
}
}