大家好,我是有一点想法的thinkmars,目前在准备面试与工作,借着间隙时间学习复习,写一点基础文章,欢迎想找工作的人与我一起学习,一起讨饭吃~
Promise的出现是异步编程的革命,自问世以后,前端再也离不开它。
一、Promise 执行机制核心原理
-
Event Loop 与微任务队列
- Promise 回调属于微任务(Microtask),与宏任务(Macrotask)的区别
- 代码执行顺序分析:
setTimeout
vsPromise.then()
的执行优先级 - 示例分析:
console.log(1); Promise.resolve().then(()=>console.log(2)); console.log(3);
-
Promise 链式调用原理
then()
方法返回新 Promise 的规则:- 返回普通值 → 触发新 Promise 的
resolve
- 返回 Promise → 直接继承其状态
- 抛出错误 → 触发新 Promise 的
reject
- 返回普通值 → 触发新 Promise 的
- 链式调用中断场景分析:中间某个
then()
抛出错误如何传递?
二、Promise 状态流转与代码路径
-
状态变化关键逻辑
resolve()
触发后,后续then()
中的回调进入微任务队列reject()
触发后,优先寻找最近的catch()
或then(onFulfilled, onRejected)
的第二个参数- 状态固化后不可变:即使再次调用
resolve/reject
无效
-
代码路径分析(高频考题)
javascriptnew Promise((resolve, reject) => { console.log(1); resolve(2); console.log(3); }) .then(val => { console.log(val); return val * 2; }) .then(val => { console.log(val); throw new Error('test'); }) .catch(err => { console.log(err.message); return 100; }) .then(val => console.log(val));
- 输出顺序分析:1 → 3 → 2 → 4 → 'test' → 100
- 关键点:同步代码立即执行,
then/catch
回调异步执行
三、错误处理关键技巧
-
catch
的穿透性- 错误会沿着链式调用传递,直到被
catch
捕获 - 示例:若中间某个
then()
没有错误处理,错误会跳过后续then()
的第一个参数
- 错误会沿着链式调用传递,直到被
-
reject
的两种写法- 显式调用
reject()
- 抛出异常:
throw new Error('msg')
(等同于reject
)
- 显式调用
-
全局异常捕获
window.addEventListener('unhandledrejection', callback)
处理未捕获的 Promise 错误
四、高阶 API 使用场景
-
Promise.all
vsPromise.allSettled
all
: 全部成功时返回结果数组,任一失败立即拒绝allSettled
: 始终等待所有 Promise 完成,返回状态描述对象- 适用场景对比(如批量请求是否允许部分失败)
-
Promise.race
竞速场景-
第一个完成的 Promise 决定结果(无论成功/失败)
-
超时控制实战代码:
javascriptconst timeout = (ms) => new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), ms) ); Promise.race([fetchData(), timeout(3000)]) .then(data => console.log(data)) .catch(err => console.log(err));
-
-
Promise.any
(ES2021)- 第一个成功的 Promise 决定结果,全部失败时抛出
AggregateError
- 第一个成功的 Promise 决定结果,全部失败时抛出
五、常见陷阱与面试题
-
嵌套 Promise 问题
- 反模式:在
then()
中嵌套 new Promise(应使用链式调用扁平化)
- 反模式:在
-
值穿透(Value Penetration)
javascriptPromise.resolve(1) .then(Promise.resolve(2)) // 参数不是函数! .then(console.log); // 输出 1
- 原因:
then()
的参数必须为函数,否则发生值穿透
- 原因:
-
同步代码中的错误
javascriptnew Promise((resolve, reject) => { throw new Error('sync error'); // 同步错误直接触发 reject }).catch(err => console.log(err));
六、Async/Await 与 Promise 的关系
-
本质是语法糖
async
函数返回 Promise,await
相当于then()
的链式调用- 错误处理:
try/catch
捕获await
后的 Promise reject
-
执行顺序对比
- 分析
async/await
与普通 Promise 链的微任务队列差异
- 分析
七、实战代码分析训练
-
混合宏任务/微任务执行顺序
javascriptsetTimeout(() => console.log(1), 0); Promise.resolve().then(() => console.log(2)); console.log(3); // 输出顺序:3 → 2 → 1
-
复杂链式调用+错误处理
javascriptPromise.resolve() .then(() => { throw new Error('err1'); }) .catch(() => console.log('catch1')) .then(() => console.log('then1')) .catch(() => console.log('catch2')); // 输出:catch1 → then1