async 函数是 ES2017 引入的异步编程终极解决方案,让异步代码写起来像同步代码一样直观。
基本语法#
定义 async 函数#
// 函数声明async function fetchData() { return '数据'}
// 函数表达式const getData = async function () { return '数据'}
// 箭头函数const loadData = async () => { return '数据'}
// 方法const obj = { async getData() { return '数据' },}
// 类方法class API { async fetch() { return '数据' }}async 函数的返回值#
async 函数总是返回一个 Promise:
async function example() { return '直接返回值'}
example().then(console.log) // '直接返回值'
// 返回值会被 Promise.resolve 包装async function returnPromise() { return Promise.resolve('已经是 Promise')}
returnPromise().then(console.log) // '已经是 Promise'
// 抛出错误会变成 rejected 状态async function throwError() { throw new Error('出错了')}
throwError().catch((err) => console.log(err.message)) // '出错了'await 表达式#
基本用法#
await 只能在 async 函数内部使用,用于等待 Promise 完成:
async function fetchUser() { // await 暂停执行,等待 Promise 完成 const response = await fetch('/api/user') const data = await response.json() return data}await 的执行顺序#
async function example() { console.log('1') const result = await Promise.resolve('2') console.log(result) console.log('3')}
console.log('start')example()console.log('end')
// 输出顺序:// start// 1// end// 2// 3🤔 await 之后的代码会在微任务队列中执行。
await 非 Promise 值#
await 可以用于任何值,非 Promise 值会被自动包装:
async function example() { const a = await 1 // 等价于 await Promise.resolve(1) const b = await '字符串' const c = await { name: '张三' }
console.log(a, b, c)}
example() // 1 '字符串' { name: '张三' }thenable 对象#
await 可以处理任何 thenable 对象:
const thenable = { then(resolve) { setTimeout(() => resolve('thenable 结果'), 100) },}
async function example() { const result = await thenable console.log(result) // 'thenable 结果'}
example()对比 Promise#
链式调用 vs async/await#
// Promise 链式调用function fetchUserPosts(userId) { return fetch(`/api/users/${userId}`) .then((response) => response.json()) .then((user) => fetch(`/api/posts?userId=${user.id}`)) .then((response) => response.json())}
// async/await 写法async function fetchUserPosts(userId) { const userResponse = await fetch(`/api/users/${userId}`) const user = await userResponse.json() const postsResponse = await fetch(`/api/posts?userId=${user.id}`) return postsResponse.json()}条件逻辑#
// Promise 方式处理条件逻辑很麻烦function getData(condition) { return fetch('/api/data') .then((response) => response.json()) .then((data) => { if (condition) { return fetch('/api/extra') .then((r) => r.json()) .then((extra) => ({ ...data, extra })) } return data })}
// async/await 更清晰async function getData(condition) { const response = await fetch('/api/data') const data = await response.json()
if (condition) { const extraResponse = await fetch('/api/extra') const extra = await extraResponse.json() return { ...data, extra } }
return data}循环中的异步#
// 错误:forEach 不会等待async function wrong(urls) { urls.forEach(async (url) => { const data = await fetch(url) console.log(data) }) console.log('完成') // 会先打印}
// 正确:串行执行async function sequential(urls) { for (const url of urls) { const response = await fetch(url) console.log(await response.json()) } console.log('完成')}
// 正确:并行执行async function parallel(urls) { const promises = urls.map((url) => fetch(url).then((r) => r.json())) const results = await Promise.all(promises) console.log(results) console.log('完成')}错误处理#
try/catch#
async function fetchData() { try { const response = await fetch('/api/data') if (!response.ok) { throw new Error(`HTTP ${response.status}`) } return await response.json() } catch (error) { console.error('获取数据失败:', error) throw error // 可以重新抛出 }}多个 await 的错误处理#
async function fetchMultiple() { try { const user = await fetchUser() const posts = await fetchPosts(user.id) const comments = await fetchComments(posts[0].id) return { user, posts, comments } } catch (error) { // 任何一个失败都会到这里 console.error('发生错误:', error) }}单独处理每个 await#
async function fetchWithIndividualHandling() { let user, posts
try { user = await fetchUser() } catch (error) { console.error('获取用户失败') user = null }
try { posts = await fetchPosts() } catch (error) { console.error('获取文章失败') posts = [] }
return { user, posts }}catch 方法结合#
async function fetchData() { // 使用 catch 提供默认值 const user = await fetchUser().catch(() => null) const posts = await fetchPosts().catch(() => [])
return { user, posts }}并发执行#
Promise.all 并发#
async function fetchAllData() { // 串行:慢 // const users = await fetchUsers(); // const posts = await fetchPosts();
// 并行:快 const [users, posts] = await Promise.all([fetchUsers(), fetchPosts()])
return { users, posts }}Promise.allSettled 容错并发#
async function fetchAllWithFallback() { const results = await Promise.allSettled([ fetchUsers(), fetchPosts(), fetchComments(), ])
return { users: results[0].status === 'fulfilled' ? results[0].value : [], posts: results[1].status === 'fulfilled' ? results[1].value : [], comments: results[2].status === 'fulfilled' ? results[2].value : [], }}竞速#
async function fetchWithTimeout(url, timeout = 5000) { return Promise.race([ fetch(url), new Promise((_, reject) => setTimeout(() => reject(new Error('请求超时')), timeout) ), ])}
async function example() { try { const response = await fetchWithTimeout('/api/data', 3000) return response.json() } catch (error) { console.error(error.message) }}实战模式#
重试机制#
async function fetchWithRetry(url, maxRetries = 3, delay = 1000) { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { const response = await fetch(url) if (!response.ok) throw new Error(`HTTP ${response.status}`) return await response.json() } catch (error) { if (attempt === maxRetries) throw error console.log(`第 ${attempt} 次失败,${delay}ms 后重试...`) await new Promise((r) => setTimeout(r, delay)) } }}串行队列#
async function processQueue(items, processor) { const results = [] for (const item of items) { results.push(await processor(item)) } return results}
// 使用const urls = ['/api/1', '/api/2', '/api/3']const results = await processQueue(urls, async (url) => { const response = await fetch(url) return response.json()})限制并发数#
async function asyncPool(limit, items, fn) { const results = [] const executing = new Set()
for (const item of items) { const promise = fn(item).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 results = await asyncPool(3, urls, async (url) => { const response = await fetch(url) return response.json()})缓存包装器#
function withCache(fn) { const cache = new Map()
return async function (...args) { const key = JSON.stringify(args)
if (cache.has(key)) { return cache.get(key) }
const result = await fn.apply(this, args) cache.set(key, result) return result }}
const cachedFetch = withCache(async (url) => { const response = await fetch(url) return response.json()})顶层 await#
ES2022 允许在模块顶层使用 await:
// config.js (ES Module)const response = await fetch('/api/config')export const config = await response.json()
// main.jsimport { config } from './config.js'console.log(config) // 已经是解析后的数据动态导入#
// 根据条件动态导入模块const locale = navigator.languageconst messages = await import(`./i18n/${locale}.js`)
// 懒加载组件const { default: Component } = await import('./Component.js')常见陷阱#
🔶 忘记 await#
async function wrong() { const data = fetchData() // 忘记 await,data 是 Promise console.log(data.name) // undefined}
async function correct() { const data = await fetchData() console.log(data.name)}🔶 不必要的 await#
// 不必要的 awaitasync function unnecessary() { return await fetchData() // await 是多余的}
// 直接 return 即可async function correct() { return fetchData()}
// 但在 try/catch 中需要 awaitasync function withTryCatch() { try { return await fetchData() // 需要 await 才能捕获错误 } catch (error) { console.error(error) }}🔶 串行 vs 并行#
// 串行:总耗时 = t1 + t2async function slow() { const a = await fetch('/api/a') const b = await fetch('/api/b') return [a, b]}
// 并行:总耗时 = max(t1, t2)async function fast() { const [a, b] = await Promise.all([fetch('/api/a'), fetch('/api/b')]) return [a, b]}小结#
| 特性 | 说明 |
|---|---|
| async | 声明异步函数,返回 Promise |
| await | 暂停执行,等待 Promise 完成 |
| 错误处理 | try/catch 或 .catch() |
| 并发 | Promise.all/allSettled/race |
| 顶层 await | ES2022 模块支持 |
async/await 是目前最推荐的异步编程方式,让代码更易读、更易维护。