前端异步核心:Promise 从入门到吃透
前言
在前端开发中,异步操作无处不在:定时器、接口请求、文件读取等。在 Promise 出现之前,我们只能依靠 ** 回调函数(callback)** 处理异步逻辑,但多个异步依次执行时就会陷入 “回调地狱”,代码可读性、可维护性极差。
本文将从零开始,带你彻底理解 Promise:从诞生背景、核心状态、基础用法、链式调用、错误捕获,到静态方法、事件循环、async/await 语法糖,覆盖日常开发与面试所有高频考点,一篇吃透 Promise。
一、为什么需要 Promise?
1. 回调函数与回调地狱
没有 Promise 时,异步操作依赖回调函数实现:
js
setTimeout(() => {
console.log('1秒后执行')
}, 1000)
当需要依次执行多个异步操作时,代码会层层嵌套,形成回调地狱(Callback Hell) :
js
setTimeout(() => {
console.log(1)
setTimeout(() => {
console.log(2)
setTimeout(() => {
console.log(3)
}, 1000)
}, 1000)
}, 1000)
2. 回调地狱的缺点
- 代码嵌套极深,可读性极差
- 错误处理混乱,难以统一捕获
- 逻辑难以复用、组合
- 业务复杂后完全难以维护
Promise 正是为解决这一问题诞生,它是专门管理异步操作的对象,能让异步代码写得像同步一样扁平清晰。
二、Promise 核心:三种状态
Promise 实例一生只有三种状态,且状态一旦改变就不可逆。
三种状态详解
- **pending(等待中)**刚创建时的默认状态,异步操作正在执行,既未成功也未失败。
- **fulfilled(已成功 / 已兑现)**异步操作执行成功,状态从
pending → fulfilled。 - **rejected(已失败 / 已拒绝)**异步操作执行失败,状态从
pending → rejected。
状态变化规则
- 仅两种合法变化:
pending → fulfilled、pending → rejected - 状态变更后固定不变,成功后不能再失败,失败后也不能再成功。
三、Promise 基础用法
1. 创建 Promise
Promise 是构造函数,需通过 new Promise() 创建,接收一个执行器函数(executor) :
js
const p = new Promise((resolve, reject) => {
// 异步逻辑写在这里
})
执行器函数包含两个核心参数:
resolve(value):调用后状态变为fulfilled,并向外传递成功结果reject(reason):调用后状态变为rejected,并向外传递失败原因
简单示例:
js
const p = new Promise((resolve, reject) => {
setTimeout(() => {
// 异步成功
resolve('请求成功')
// 异步失败
// reject('请求失败')
}, 1000)
})
这一步仅创建并启动异步,无法直接获取结果。
2. 获取结果:.then () 和 .catch ()
通过 .then() 接收成功结果,.catch() 接收失败原因:
js
p
.then(res => {
console.log('成功:', res)
})
.catch(err => {
console.log('失败:', err)
})
.then()对应resolve().catch()对应reject()
四、Promise 核心能力:链式调用
链式调用是 Promise 解决回调地狱的关键,让异步代码保持扁平结构。
1. 链式调用原理
- 每个
.then()都会返回一个新的 Promise - 下一个
.then()接收上一个.then()的返回值 - 可无限链式书写,逻辑清晰
2. 链式调用示例
js
new Promise((resolve) => {
setTimeout(() => {
console.log('第一步')
resolve(1)
}, 1000)
})
.then(res => {
console.log('接收:', res)
return new Promise(resolve => {
setTimeout(() => {
console.log('第二步')
resolve(2)
}, 1000)
})
})
.then(res => {
console.log('接收:', res)
console.log('第三步')
})
3. 简写:返回普通值
在 .then() 中直接 return 普通值,会自动包装为 Promise.resolve(值):
js
Promise.resolve()
.then(() => {
return 123
})
.then(res => {
console.log(res) // 123
})
五、Promise 错误捕获机制
Promise 支持两种错误捕获方式,推荐统一使用 .catch()。
1. 方式一:then 第二个参数
仅能捕获当前 Promise 的错误,无法捕获整条链:
js
new Promise((resolve, reject) => {
reject('出错了')
})
.then(
res => {},
err => { console.log('失败:', err) }
)
2. 方式二:统一 catch(推荐)
可捕获整条链式调用中任意位置的错误:
js
new Promise((resolve, reject) => {
reject('出错了')
})
.then(res => {})
.catch(err => {
console.log('捕获错误:', err)
})
3. 捕获特点
- 任意环节报错,会跳过后续所有 then,直接进入 catch
- catch 执行后,仍可继续链式调用 then
- 可捕获:reject、throw new Error、then 内代码异常
六、Promise 常用静态方法
Promise 提供多个直接调用的静态方法,无需 new,是面试与开发高频考点。
1. Promise.resolve()
快速创建一个直接成功的 Promise:
js
Promise.resolve(100).then(res => console.log(res)) // 100
2. Promise.reject()
快速创建一个直接失败的 Promise:
js
Promise.reject('错误').catch(err => console.log(err)) // 错误
3. Promise.all()
接收 Promise 数组,全部成功才成功,任一失败则整体失败:
- 成功:返回与传入顺序一致的结果数组
- 失败:返回第一个失败的原因
js
const p1 = Promise.resolve(1)
const p2 = Promise.resolve(2)
Promise.all([p1, p2]).then(res => console.log(res)) // [1,2]
4. Promise.race()
“赛跑机制”:谁先完成(成功 / 失败)就返回谁,常用于接口超时控制。
5. Promise.allSettled()
等待所有 Promise 执行结束,无论成功失败,均返回包含状态与结果的数组,适合批量请求场景。
6. Promise.any()
与 all 相反:任一成功则整体成功,全部失败才最终失败,失败时抛出 AggregateError。
七、finally 与 微任务机制
1. finally 用法
无论 Promise 成功或失败,.finally() 内逻辑最终一定执行,常用于关闭加载状态、清理资源:
js
new Promise((resolve) => resolve('ok'))
.then(res => {})
.catch(err => {})
.finally(() => {
console.log('无论如何都执行')
})
2. Promise 与微任务(Event Loop 必考)
JavaScript 是单线程语言,依靠事件循环(Event Loop)执行异步任务,执行优先级:同步任务 → 微任务 → 宏任务
- 微任务:Promise、queueMicrotask、async/await
- 宏任务:setTimeout、setInterval、ajax 等
经典面试题:
js
console.log(1)
setTimeout(() => console.log(2), 0)
Promise.resolve().then(() => console.log(3))
console.log(4)
执行顺序:1 → 4 → 3 → 2
3. 中断 Promise 链
在 .then() 中返回一个永久 pending 的 Promise,即可中断后续链式调用:
js
.then(() => {
return new Promise(() => {})
})
.then(() => {
// 永远不会执行
})
八、async/await:Promise 终极语法糖
async/await 是 Promise 的语法糖,让异步代码写法完全趋近于同步,是目前前端开发主流写法。
1. 基础规则
async写在函数前,函数内可使用awaitawait后只能跟 Promise 或 thenable 对象await会暂停执行,直到 Promise 返回结果
2. 基础示例
js
// 封装返回 Promise 的异步函数
function delay(time) {
return new Promise(resolve => {
setTimeout(() => resolve('完成'), time)
})
}
// async/await 调用
async function test() {
console.log('开始')
const res = await delay(1000)
console.log(res)
console.log('结束')
}
test()
3. async 函数返回值
- return 普通值 → 自动包装为
Promise.resolve(值) - 抛出错误 → 自动包装为
Promise.reject(错误)
4. 错误处理
推荐使用 try/catch 统一捕获:
js
async function test() {
try {
const res = await Promise.reject('出错')
} catch (err) {
console.log(err)
}
}
5. 并行执行优化
多个异步串行会浪费性能,使用 Promise.all 实现并行:
js
const [res1, res2, res3] = await Promise.all([p1(), p2(), p3()])
结语
Promise 是前端异步编程的基石,从解决回调地狱,到链式调用、统一错误处理,再到 async/await 语法糖,贯穿日常开发与面试全过程。
本文覆盖 Promise 所有核心知识点,吃透后可轻松应对接口请求、并发控制、面试手撕源码等场景,是前端进阶必备技能。