JavaScript 异步与 Promise 笔记
1. JavaScript 异步机制概览
1.1 执行栈与事件循环
- JS 是单线程执行
- 执行栈(Call Stack):存储同步任务
- 任务队列(Task Queue / Microtask Queue):
- 宏任务(Macro Task):setTimeout、setInterval、I/O、UI 渲染
- 微任务(Micro Task):Promise.then/catch/finally、MutationObserver
- 事件循环(Event Loop):
- 执行同步任务直到栈空
- 执行微任务队列
- 执行宏任务队列
- 重复循环
1.2 异步类型示意
| 类型 |
示例 |
队列类型 |
| 微任务 |
Promise.then, MutationObserver |
Microtask |
| 宏任务 |
setTimeout, setInterval, I/O |
Macrotask |
1 2 3 4 5 6 7 8 9 10
| console.log('script start');
setTimeout(() => console.log('setTimeout'), 0);
Promise.resolve().then(() => console.log('promise1')) .then(() => console.log('promise2'));
console.log('script end');
|
2. Promise 基础与用法
2.1 创建 Promise
1 2 3 4
| const promise = new Promise((resolve, reject) => { setTimeout(() => resolve('success'), 1000); });
|
2.2 Promise 状态
- Pending:初始状态
- Fulfilled:成功
- Rejected:失败
状态只能从 Pending 变为 Fulfilled 或 Rejected,且不可逆
2.3 Promise 用法
1 2 3 4
| promise .then(result => console.log(result)) .catch(error => console.error(error)) .finally(() => console.log('done'));
|
2.4 Promise 链式调用
1 2 3 4 5 6
| Promise.resolve(1) .then(val => val + 1) .then(val => { console.log(val); return val + 1; }) .then(val => console.log(val));
|
2.5 Promise.all / allSettled / race / any
1 2 3 4 5 6 7 8 9
| const p1 = Promise.resolve(1); const p2 = Promise.resolve(2); const p3 = Promise.reject('error');
Promise.all([p1, p2]).then(console.log); Promise.allSettled([p1, p3]).then(console.log);
Promise.race([p1, p2]).then(console.log); Promise.any([p3, p2]).then(console.log);
|
3. async / await 高阶用法
3.1 基本语法
1 2 3 4
| async function fetchData() { const result = await promise; console.log(result); }
|
3.2 错误处理
1 2 3 4 5 6 7 8 9
| async function test() { try { await Promise.reject('error'); } catch (err) { console.error(err); } finally { console.log('finally'); } }
|
3.3 并发与串行
1 2
| const result1 = await asyncFunc1(); const result2 = await asyncFunc2(result1);
|
1
| const [result1, result2] = await Promise.all([asyncFunc1(), asyncFunc2()]);
|
4. 底层原理与微任务机制
4.1 Promise.then 微任务原理
- then/catch/finally 会在微任务队列中注册回调
- 微任务优先于宏任务执行
1 2 3 4 5 6 7 8 9
| console.log(1);
setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
console.log(4);
|
4.2 Promise 内部执行流程
- 创建 Promise,状态 Pending
- then 注册回调函数到微任务队列
- resolve/reject 改变状态,异步执行注册回调
4.3 async/await 本质
- async 函数返回 Promise
- await 后面跟 Promise,本质是
Promise.then(...)
- 可以理解为语法糖,更直观处理异步逻辑
5. 常见异步陷阱
忘记 return promise 链
1 2
| promise.then(val => { val + 1 });
|
在 forEach 中使用 await
1 2 3
| arr.forEach(async item => { await doSomething(item); });
|
- 推荐使用
for...of 或 map + Promise.all
- Promise 内抛异常未捕获
1 2 3
| new Promise((resolve, reject) => { throw new Error('err'); }) .then(() => console.log('ok'));
|
6. 高级优化策略
批量异步请求
1
| const results = await Promise.all(requests.map(req => fetch(req)));
|
并发限制
1 2 3 4 5 6 7 8 9 10 11 12
| async function limitConcurrent(tasks, limit) { const results = []; const executing = []; for (const task of tasks) { const p = task(); results.push(p); const e = p.finally(() => executing.splice(executing.indexOf(e),1)); executing.push(e); if (executing.length >= limit) await Promise.race(executing); } return Promise.all(results); }
|
取消异步请求
- 使用
AbortController 或第三方库(axios cancel token)
7. 面试要点
- 异步队列与事件循环原理
- Promise 状态与链式调用
- async/await 与 Promise 的关系
- 微任务与宏任务区别
- 并发控制与错误处理
- 常见坑:forEach + await、未 return Promise、异常未捕获