Skip to content

Promise 进阶

在掌握 Promise 基础后,来看更多静态方法和进阶用法。

Promise.all()#

等待所有 Promise 完成,任意一个失败就立即失败:

const p1 = Promise.resolve(1)
const p2 = Promise.resolve(2)
const p3 = Promise.resolve(3)
Promise.all([p1, p2, p3]).then((values) => {
console.log(values) // [1, 2, 3](顺序与传入一致)
})
// 有一个失败就全部失败
const p4 = Promise.reject(new Error('失败'))
Promise.all([p1, p2, p4]).catch((error) => {
console.log(error.message) // '失败'
})

实际应用#

// 并行请求多个接口
async function fetchAllData() {
const [users, posts, comments] = await Promise.all([
fetch('/api/users').then((r) => r.json()),
fetch('/api/posts').then((r) => r.json()),
fetch('/api/comments').then((r) => r.json()),
])
return { users, posts, comments }
}
// 批量处理
async function processItems(items) {
const results = await Promise.all(items.map((item) => processItem(item)))
return results
}

🔶 注意事项#

// 空数组立即 resolve
Promise.all([]).then((v) => console.log(v)) // []
// 非 Promise 值会被自动包装
Promise.all([1, 2, 3]).then((v) => console.log(v)) // [1, 2, 3]
// 一旦有一个失败,不会等待其他
const slow = new Promise((r) => setTimeout(() => r('slow'), 3000))
const fast = Promise.reject(new Error('fast'))
Promise.all([slow, fast]).catch((e) => console.log(e.message)) // 'fast'(不会等待 slow)

Promise.race()#

返回最先完成(无论成功还是失败)的结果:

const p1 = new Promise((r) => setTimeout(() => r('p1'), 100))
const p2 = new Promise((r) => setTimeout(() => r('p2'), 200))
Promise.race([p1, p2]).then((value) => console.log(value)) // 'p1'
// 超时控制
function fetchWithTimeout(url, timeout = 5000) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), timeout)
),
])
}

空数组永远 pending#

Promise.race([]).then(() => {
console.log('永远不会执行')
})

Promise.allSettled()#

ES2020 新增,等待所有 Promise 完成,无论成功失败:

const p1 = Promise.resolve(1)
const p2 = Promise.reject(new Error('失败'))
const p3 = Promise.resolve(3)
Promise.allSettled([p1, p2, p3]).then((results) => {
console.log(results)
// [
// { status: 'fulfilled', value: 1 },
// { status: 'rejected', reason: Error: 失败 },
// { status: 'fulfilled', value: 3 }
// ]
})

实际应用#

// 批量请求,即使部分失败也要继续
async function fetchMultiple(urls) {
const results = await Promise.allSettled(
urls.map((url) => fetch(url).then((r) => r.json()))
)
const successful = results
.filter((r) => r.status === 'fulfilled')
.map((r) => r.value)
const failed = results
.filter((r) => r.status === 'rejected')
.map((r) => r.reason)
return { successful, failed }
}

Promise.any()#

ES2021 新增,返回第一个成功的结果:

const p1 = Promise.reject(new Error('失败1'))
const p2 = new Promise((r) => setTimeout(() => r('成功'), 100))
const p3 = Promise.reject(new Error('失败2'))
Promise.any([p1, p2, p3]).then((value) => console.log(value)) // '成功'
// 全部失败时抛出 AggregateError
Promise.any([
Promise.reject(new Error('1')),
Promise.reject(new Error('2')),
]).catch((error) => {
console.log(error instanceof AggregateError) // true
console.log(error.errors) // [Error: 1, Error: 2]
})

实际应用#

// 从多个源获取数据,使用最快的
async function fetchFromAnyMirror(mirrors) {
try {
return await Promise.any(
mirrors.map((url) => fetch(url).then((r) => r.json()))
)
} catch (error) {
throw new Error('所有镜像都失败')
}
}

错误处理模式#

统一错误处理#

class APIError extends Error {
constructor(message, status) {
super(message)
this.name = 'APIError'
this.status = status
}
}
async function fetchAPI(url) {
const response = await fetch(url)
if (!response.ok) {
throw new APIError(`请求失败: ${response.statusText}`, response.status)
}
return response.json()
}
// 使用
fetchAPI('/api/users')
.then((data) => console.log(data))
.catch((error) => {
if (error instanceof APIError) {
console.log(`API 错误 (${error.status}): ${error.message}`)
} else {
console.log('未知错误:', error)
}
})

try-catch 模式#

async function safeExecute(fn) {
try {
const result = await fn()
return { success: true, data: result }
} catch (error) {
return { success: false, error }
}
}
const result = await safeExecute(() => fetchAPI('/api/users'))
if (result.success) {
console.log(result.data)
} else {
console.log('错误:', result.error)
}

Go 风格错误处理#

function to(promise) {
return promise.then((data) => [null, data]).catch((err) => [err, null])
}
async function example() {
const [err, data] = await to(fetchAPI('/api/users'))
if (err) {
console.log('错误:', err)
return
}
console.log('数据:', data)
}

并发控制#

限制并发数#

async function asyncPool(limit, items, fn) {
const results = []
const executing = new Set()
for (const [index, item] of items.entries()) {
const promise = fn(item, index).then((result) => {
executing.delete(promise)
return result
})
results.push(promise)
executing.add(promise)
if (executing.size >= limit) {
await Promise.race(executing)
}
}
return Promise.all(results)
}
// 使用:同时最多 3 个并发请求
const urls = ['url1', 'url2', 'url3', 'url4', 'url5']
const results = await asyncPool(3, urls, (url) => fetch(url))

队列模式#

class PromiseQueue {
constructor(concurrency = 1) {
this.concurrency = concurrency
this.running = 0
this.queue = []
}
add(fn) {
return new Promise((resolve, reject) => {
this.queue.push({ fn, resolve, reject })
this.run()
})
}
run() {
while (this.running < this.concurrency && this.queue.length > 0) {
const { fn, resolve, reject } = this.queue.shift()
this.running++
fn()
.then(resolve)
.catch(reject)
.finally(() => {
this.running--
this.run()
})
}
}
}
// 使用
const queue = new PromiseQueue(2)
for (const url of urls) {
queue.add(() => fetch(url)).then((response) => console.log('完成:', url))
}

Promise 实现细节#

微任务#

Promise 回调在微任务队列执行:

console.log('1')
Promise.resolve().then(() => {
console.log('2')
})
console.log('3')
// 输出顺序: 1, 3, 2

值穿透#

Promise.resolve(1)
.then() // 没有回调函数
.then(2) // 非函数参数被忽略
.then((v) => console.log(v)) // 1(值穿透)

Promise 链的错误恢复#

Promise.reject(new Error('失败'))
.catch((error) => {
console.log('捕获:', error.message)
return '恢复的值' // catch 返回值会被传递
})
.then((value) => {
console.log('继续:', value) // '恢复的值'
})

方法对比#

方法版本行为
Promise.all()ES6全部成功或任一失败
Promise.race()ES6返回最先完成的
Promise.allSettled()ES2020等待全部完成
Promise.any()ES2021返回第一个成功的
// 对比
const p1 = Promise.resolve(1)
const p2 = Promise.reject(2)
const p3 = Promise.resolve(3)
Promise.all([p1, p2, p3]) // reject 2
Promise.race([p1, p2, p3]) // resolve 1
Promise.allSettled([p1, p2, p3]) // resolve [...]
Promise.any([p1, p2, p3]) // resolve 1

Promise 是 JavaScript 异步编程的核心,熟练掌握各种方法和模式对于编写健壮的异步代码至关重要。