核心概念 JavaScript 是单线程语言,但通过异步编程实现非阻塞操作。主要包括:
- Promise - 异步操作的标准化解决方案
- async/await - Promise 的语法糖
- 事件循环 - JavaScript 的执行机制
一、Promise
1.1 为什么需要 Promise?
回调地狱问题 异步任务嵌套多层会导致代码难以维护:
// ❌ 回调地狱
getData(function (a) {
getMoreData(a, function (b) {
getMoreData(b, function (c) {
getMoreData(c, function (d) {
// 无限嵌套...
})
})
})
})ES6 引入 Promise 对象,提供优雅的异步编程解决方案。
1.2 Promise 的三种状态
状态说明
- pending(等待):初始状态,既没有成功也没有失败
- fulfilled(已成功):操作成功完成
- rejected(已失败):操作失败
stateDiagram-v2 [*] --> pending: 创建 Promise pending --> fulfilled: resolve() pending --> rejected: reject() fulfilled --> [*] rejected --> [*]
状态特点
- 状态不可逆,一旦改变就不会再变
resolve和reject只执行一个
1.3 基本用法
// 创建 Promise
const promise = new Promise((resolve, reject) => {
// 异步操作
const success = true
if (success) {
resolve("成功的数据") // 将状态改为 fulfilled
} else {
reject("失败的原因") // 将状态改为 rejected
}
})
// 使用 Promise
promise
.then((data) => {
console.log(data) // "成功的数据"
})
.catch((error) => {
console.error(error)
})
.finally(() => {
console.log("无论成功失败都会执行")
})1.4 Promise 静态方法
| 方法 | 说明 | 返回值 |
|---|---|---|
Promise.resolve() | 创建一个已解决的 Promise | resolved Promise |
Promise.reject() | 创建一个已拒绝的 Promise | rejected Promise |
Promise.all() | 全部成功才成功,有一个失败就失败 | Promise |
Promise.allSettled() | 等待全部完成(不管成功失败) | Promise |
Promise.race() | 返回最先完成的结果 | Promise |
Promise.any() | 返回第一个成功的结果 | Promise |
Promise.all() - 全部成功
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("失败")
Promise.all([p1, p2, p4]).catch((error) => {
console.log(error) // "失败"
})Promise.race() - 赛跑
const p1 = new Promise((resolve) => setTimeout(() => resolve("A"), 100))
const p2 = new Promise((resolve) => setTimeout(() => resolve("B"), 200))
Promise.race([p1, p2]).then((value) => {
console.log(value) // "A"(最快完成的)
})Promise.allSettled() - 全部完成
const p1 = Promise.resolve(1)
const p2 = Promise.reject("失败")
const p3 = Promise.resolve(3)
Promise.allSettled([p1, p2, p3]).then((results) => {
console.log(results)
// [
// { status: 'fulfilled', value: 1 },
// { status: 'rejected', reason: '失败' },
// { status: 'fulfilled', value: 3 }
// ]
})1.5 Promise 链式调用
fetchUser()
.then((user) => {
console.log("获取用户:", user)
return fetchPosts(user.id) // 返回新的 Promise
})
.then((posts) => {
console.log("获取文章:", posts)
return fetchComments(posts[0].id)
})
.then((comments) => {
console.log("获取评论:", comments)
})
.catch((error) => {
console.error("发生错误:", error)
})链式调用原理
then()方法返回一个新的 Promise- 可以通过
return值传递给下一个then()
1.6 Promise 常见问题
错误处理
// ✅ 推荐:使用 catch
promise
.then((data) => {})
.catch((error) => {
console.error(error)
})
// ⚠️ 注意:then 的第二个参数也可以捕获错误
promise.then(
(data) => {},
(error) => {}, // 但无法捕获前面 then 中的错误
)
// ✅ 推荐:try/catch (async/await)
async function handle() {
try {
const data = await promise
} catch (error) {
console.error(error)
}
}Promise 穿透
Promise.resolve(1)
.then(null) // 空的 then 会透传值
.then((value) => console.log(value)) // 1二、async/await
async/await 是 Promise 的语法糖
- async 函数:返回 Promise
- await 表达式:等待 Promise 完成
2.1 基本语法
// async 函数声明
async function fetchData() {
return "数据" // 自动包装成 Promise.resolve("数据")
}
// 等同于
function fetchData() {
return Promise.resolve("数据")
}2.2 await 使用
async function getData() {
// 等待 Promise 完成
const data = await fetchUser()
// 等待另一个 Promise
const posts = await fetchPosts(data.id)
return posts
}
// 使用
getData().then((posts) => {
console.log(posts)
})await 只能在 async 函数内使用
// ❌ 错误 function test() { await promise; // SyntaxError } // ✅ 正确 async function test() { await promise; }
2.3 错误处理
async function fetchData() {
try {
const data = await fetchUser()
console.log(data.message)
} catch (error) {
console.error("Error:", error.message)
} finally {
console.log("Finally block executed")
}
}2.4 并发处理
// ❌ 串行执行(慢)
async function serial() {
const a = await fetchA() // 等待 1s
const b = await fetchB() // 等待 1s
const c = await fetchC() // 等待 1s
// 总共 3s
}
// ✅ 并行执行(快)
async function parallel() {
const [a, b, c] = await Promise.all([
fetchA(), // 1s
fetchB(), // 1s
fetchC(), // 1s
])
// 总共 1s
}
// ✅ 互不依赖的并发
async function independent() {
const p1 = fetchA()
const p2 = fetchB()
const a = await p1
const b = await p2
}2.5 async/await vs Promise
| 特性 | async/await | Promise |
|---|---|---|
| 可读性 | 高(同步写法) | 低(链式调用) |
| 错误处理 | try/catch | .catch() |
| 中间值处理 | 方便 | 需要在外层作用域 |
| 调试 | 同步调试 | 断点跳跃 |
推荐使用 async/await 代码更简洁,错误处理更直观
三、事件循环(Event Loop)
3.1 JavaScript 是单线程的
为什么需要事件循环? JavaScript 是单线程语言,同一时间只能做一件事。为了处理异步操作,使用事件循环机制。
3.2 任务分类
同步代码 vs 异步代码
// 同步代码:立即执行
console.log(1)
// 异步代码:稍后执行
setTimeout(() => console.log(2), 0)宏任务 vs 微任务
| 类型 | 说明 | 示例 |
|---|---|---|
| 宏任务 | 宿主环境(浏览器)发起的任务 | setTimeout、setInterval、UI 渲染、DOM 事件 |
| 微任务 | JavaScript 引擎发起的任务 | Promise.then、MutationObserver、queueMicrotask |
3.3 事件循环执行顺序
graph TD A[开始] --> B[执行同步代码] B --> C[同步代码执行完毕?] C -->|否| B C -->|是| D[执行微任务队列] D --> E[微任务队列为空?] E -->|否| D E -->|是| F[从宏任务队列取一个] F --> G[执行宏任务] G --> B
console.log("1. 开始")
setTimeout(() => {
console.log("2. 宏任务:setTimeout")
}, 0)
Promise.resolve().then(() => {
console.log("3. 微任务:Promise")
})
console.log("4. 结束")
// 输出顺序:
// 1. 开始(同步)
// 4. 结束(同步)
// 3. 微任务:Promise
// 2. 宏任务:setTimeout3.4 执行优先级
process.nextTick(Node.js) > Promise.then > setTimeout > setImmediate
注意事项
new Promise()中的代码是同步执行的then/catch回调是异步的requestAnimationFrame和定时器顺序不一定
3.5 经典面试题
setTimeout(() => console.log(1), 0)
new Promise((resolve) => {
console.log(2) // 同步代码
resolve()
console.log(3) // 同步代码
}).then(() => {
console.log(4) // 微任务
})
console.log(5) // 同步代码
// 输出顺序:2, 3, 5, 4, 1解析:
- 同步代码:
console.log(2)→console.log(3)→console.log(5) - 微任务队列:
then回调 - 宏任务队列:
setTimeout回调 - 清空微任务:
console.log(4) - 执行宏任务:
console.log(1)
四、最佳实践
异步编程建议
- 优先使用 async/await 而非 Promise 链式调用
- 并发请求使用
Promise.all()- 总是使用 try/catch 捕获错误
- 避免在循环中 await,使用
Promise.all()- 理解事件循环,避免阻塞主线程
// ✅ 推荐写法
async function fetchUsers() {
try {
const [users, posts] = await Promise.all([fetch("/api/users"), fetch("/api/posts")])
return { users, posts }
} catch (error) {
console.error("请求失败:", error)
throw error
}
}