JavaScript异步,笔记250810
JavaScript 异步编程全面解析
一、异步编程基础
为什么需要异步编程? JavaScript 是单线程语言,异步操作可以防止阻塞主线程:
- 处理网络请求
- 执行文件 I/O
- 管理定时任务
- 响应用户交互
// 同步阻塞示例
console.log("开始");
alert("阻塞操作"); // 阻塞主线程
console.log("结束");
// 异步非阻塞示例
console.log("开始");
setTimeout(() => console.log("异步操作"), 0);
console.log("结束");
// 输出:开始 → 结束 → 异步操作
事件循环机制 JavaScript 引擎通过事件循环处理异步操作:
graph LR
A[调用栈] --> B{调用栈为空?}
B -->|是| C[事件队列]
C --> D[取第一个任务]
D --> E[执行任务]
E --> A
B -->|否| A
二、异步解决方案演进
1. 回调函数(Callback)
function fetchData(callback) {
setTimeout(() => callback('数据'), 1000);
}
fetchData(data => console.log(data));
回调地狱问题:
getUser(id, user => {
getProfile(user.id, profile => {
getOrders(profile.id, orders => {
renderUI(user, profile, orders);
}, handleError);
}, handleError);
}, handleError);
2. Promise(ES6)
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
Math.random() > 0.5
? resolve('成功数据')
: reject('请求失败');
}, 1000);
});
}
fetchData()
.then(data => console.log(data))
.catch(err => console.error(err));
3. async/await(ES2017)
async function loadData() {
try {
const data = await fetchData();
console.log(data);
} catch (err) {
console.error(err);
}
}
三、Promise 深度解析
Promise 生命周期
stateDiagram-v2
[*] --> Pending
Pending --> Fulfilled: resolve()
Pending --> Rejected: reject()
Fulfilled --> [*]
Rejected --> [*]
核心方法
// 链式调用
fetchData()
.then(process)
.then(save)
.catch(handleError)
.finally(cleanup);
// 并行执行
Promise.all([task1, task2, task3])
.then(results => console.log(results));
// 竞速执行
Promise.race([fastSource, slowSource])
.then(firstResult => use(firstResult));
// 全完成执行
Promise.allSettled([successTask, failTask])
.then(results => results.forEach(handleResult));
四、async/await 最佳实践
基础用法
async function getUserData(userId) {
try {
const user = await fetchUser(userId);
const orders = await fetchOrders(user.id);
return { user, orders };
} catch (error) {
console.error("获取数据失败:", error);
return { user: null, orders: [] };
} finally {
console.log("请求完成");
}
}
优化技巧
// 顺序 vs 并行
async function getData() {
// 顺序执行 (慢)
const a = await fetchA();
const b = await fetchB();
// 并行执行 (快)
const [c, d] = await Promise.all([fetchC(), fetchD()]);
return { a, b, c, d };
}
// 尽早启动请求
async function optimizedLoad() {
const userPromise = fetchUser(); // 立即启动
const config = await fetchConfig(); // 等待配置
const user = await userPromise; // 等待用户数据
return { user, config };
}
五、高级异步模式
1. 取消异步操作
function cancellableFetch(url) {
const controller = new AbortController();
const promise = fetch(url, {
signal: controller.signal
});
return {
promise,
cancel: () => controller.abort()
};
}
// 使用
const { promise, cancel } = cancellableFetch('/api/data');
cancelButton.onclick = cancel;
try {
const data = await promise;
} catch (err) {
if (err.name === 'AbortError') {
console.log('请求已取消');
}
}
2. 自动重试机制
async function fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
return await fetch(url);
} catch (err) {
if (i === retries - 1) throw err;
await new Promise(r => setTimeout(r, 1000 * (i + 1))); // 指数退避
}
}
}
3. 并发控制
async function parallelWithLimit(tasks, limit = 5) {
const results = [];
const executing = new Set();
for (const task of tasks) {
const p = task().then(result => {
results.push(result);
executing.delete(p);
});
executing.add(p);
if (executing.size >= limit) {
await Promise.race(executing);
}
}
await Promise.all(executing);
return results;
}
// 使用
const tasks = Array(10).fill(() => fetchData());
const results = await parallelWithLimit(tasks, 3);
六、错误处理策略
分层错误处理
async function purchaseProduct(userId, productId) {
try {
const user = await validateUser(userId);
} catch (err) {
if (err.code === 'USER_INVALID') {
return { success: false, reason: '用户无效' };
}
throw err; // 重新抛出未知错误
}
try {
await deductBalance(userId, productId);
await deliverProduct(productId);
return { success: true };
} catch (err) {
await rollbackTransaction();
logError(err);
throw new Error('购买失败');
}
}
全局错误捕获
// 浏览器
window.addEventListener('unhandledrejection', event => {
console.error('未处理的Promise拒绝:', event.reason);
event.preventDefault();
});
// Node.js
process.on('unhandledRejection', (reason, promise) => {
console.error('未处理的拒绝:', reason);
});
七、性能优化技巧
1. 避免阻塞事件循环
// 分解长任务
async function processLargeData() {
for (let i = 0; i < 100; i++) {
await processChunk(i);
await yieldToEventLoop(); // 让出事件循环
}
}
function yieldToEventLoop() {
return new Promise(resolve => setTimeout(resolve, 0));
}
2. 内存管理
// 及时释放资源
async function loadImage(url) {
const response = await fetch(url);
const blob = await response.blob();
const img = new Image();
img.src = URL.createObjectURL(blob);
// 使用后释放内存
img.onload = () => URL.revokeObjectURL(img.src);
return img;
}
八、实战应用示例
实时搜索优化
let searchController = null;
async function handleSearch(query) {
// 取消之前的请求
if (searchController) searchController.abort();
const controller = new AbortController();
searchController = controller;
try {
const response = await fetch(`/search?q=${query}`, {
signal: controller.signal
});
const results = await response.json();
displayResults(results);
} catch (err) {
if (err.name !== 'AbortError') {
showError('搜索失败');
}
}
}
表单提交处理
async function handleSubmit(e) {
e.preventDefault();
const submitBtn = e.target.querySelector('button[type="submit"]');
submitBtn.disabled = true;
try {
const formData = new FormData(e.target);
const response = await fetch('/submit', {
method: 'POST',
body: formData
});
if (!response.ok) throw new Error('提交失败');
showSuccess();
} catch (err) {
showError(err.message);
} finally {
submitBtn.disabled = false;
}
}
九、常见陷阱与解决方案
1. 异步循环问题
// 错误:顺序执行异步循环
async function processArray(array) {
for (const item of array) {
await processItem(item); // 顺序执行,效率低
}
}
// 正确:并行处理
async function processArray(array) {
await Promise.all(array.map(item => processItem(item)));
}
2. Promise 创建陷阱
// 错误:立即执行异步操作
const promises = [fetchA, fetchB].map(fn => {
return new Promise(resolve => {
fn().then(resolve); // 不必要的 Promise 包装
});
});
// 正确:直接返回 Promise
const promises = [fetchA(), fetchB()];
3. async/await 误用
// 错误:不必要的 async
async function getConfig() {
return { setting: 'value' }; // 自动包装为 Promise
}
// 正确:直接返回值
function getConfig() {
return { setting: 'value' };
}
十、现代异步编程总结
技术选型指南
| 场景 | 推荐方案 |
|---|---|
| 简单异步操作 | Promise 或 async/await |
| 复杂异步流程 | async/await + try/catch |
| 并行多个独立请求 | Promise.all + await |
| 需要取消的操作 | AbortController + Promise |
| 流式数据处理 | 异步迭代器 (for-await-of) |
| 高并发控制 | 自定义并发池 |
| 需要重试的操作 | 递归 async 函数 |
终极最佳实践
async function optimalAsyncWorkflow() {
// 1. 并行加载独立数据
const [user, config] = await Promise.all([
fetchUser(),
fetchConfig()
]);
// 2. 顺序执行依赖操作
const profile = await fetchProfile(user.id);
// 3. 后台执行非关键任务
sendAnalytics(user).catch(logError);
// 4. 返回关键数据
return { user, profile, config };
}
// 5. 全局错误处理
optimalAsyncWorkflow()
.then(renderUI)
.catch(showErrorScreen)
.finally(cleanupResources);
掌握 JavaScript 异步编程需要理解:
- 事件循环机制
- Promise 原理和链式调用
- async/await 执行流程
- 错误处理策略
- 性能优化技巧
通过合理选择异步模式并遵循最佳实践,可以构建高性能、可维护的 JavaScript 应用。
1️⃣ 参考 1
JavaScript 异步编程是处理非阻塞操作的核心机制,以下是关键知识点和最佳实践总结:
异步编程的必要性 JavaScript 是单线程语言,异步模型避免阻塞主线程:
- 网络请求(
fetch) - 文件操作(Node.js)
- 定时任务(
setTimeout/setInterval) - 用户交互事件(点击/滚动)
核心解决方案演进
| 方案 | 特点 | 代码示例 |
|---|---|---|
| 回调函数 | 基础方案,易产生回调地狱 | fs.readFile('file.txt', callback) |
| Promise | 链式调用,解决嵌套问题 | fetch(url).then().catch() |
| async/await | 同步写法,最佳可读性 | const data = await fetch() |
Promise 深度解析
// 创建 Promise
const promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("成功!"), 1000);
});
// 使用链式调用
promise
.then(result => result + " 再处理")
.then(final => console.log(final)) // "成功! 再处理"
.catch(err => console.error(err));
关键方法:
Promise.all([...]):并行执行,全部成功Promise.race([...]):竞速,第一个完成的结果Promise.any([...]):第一个成功的值Promise.allSettled([...]):所有结果(无论成败)
async/await 最佳实践
async function loadData() {
try {
const user = await fetchUser();
const posts = await fetchPosts(user.id);
return { user, posts };
} catch (error) {
console.error("加载失败:", error);
throw error; // 重新抛出错误
} finally {
console.log("清理资源");
}
}
// 使用
loadData().then(data => renderUI(data));
使用技巧:
- 用
try/catch替代.catch() - 避免在循环中误用
await(需并行时用Promise.all)// 错误:顺序执行(慢) for (const url of urls) await fetch(url); // 正确:并行执行 await Promise.all(urls.map(url => fetch(url)));
事件循环(Event Loop)机制 JavaScript 运行时模型:
console.log("Start");
setTimeout(() => console.log("Timeout"), 0);
Promise.resolve()
.then(() => console.log("Microtask 1"))
.then(() => console.log("Microtask 2"));
console.log("End");
/* 输出顺序:
Start
End
Microtask 1
Microtask 2
Timeout
*/
执行优先级:
- 同步代码(主线程)
- 微任务(Microtasks):
Promise回调queueMicrotask()- MutationObserver
- 宏任务(Macrotasks):
setTimeout/setInterval- I/O 操作
- UI 渲染
常见陷阱与解决方案
- 回调地狱 → 用 Promise 链或 async/await 扁平化
- 未处理的拒绝 → 添加
.catch()或全局错误监听process.on('unhandledRejection', error => { /* 处理 */ }); - 并发控制 → 限制并行数量
// 使用 p-limit 库 const limit = pLimit(3); // 最大并发数 await Promise.all(items.map(item => limit(() => processItem(item)) );
现代异步模式
- 异步迭代(Async Iteration):
for await (const chunk of readableStream) { console.log(chunk); } - Web Workers:
const worker = new Worker('task.js'); worker.postMessage(data); worker.onmessage = e => handle(e.data); - 响应式编程(RxJS):
import { fromEvent } from 'rxjs'; fromEvent(button, 'click') .pipe(throttleTime(1000)) .subscribe(() => handleClick());
最佳实践总结
- 首选
async/await编写异步逻辑 - 并行任务用
Promise.all()而非顺序await - 始终处理错误(
try/catch或.catch()) - 避免阻塞事件循环(长任务拆分为微任务)
- 复杂场景考虑 RxJS 或 Web Workers
示例:健壮的异步函数
async function fetchWithRetry(url, retries = 3) { try { const response = await fetch(url); if (!response.ok) throw new Error(`HTTP ${response.status}`); return response.json(); } catch (error) { if (retries <= 0) throw error; await new Promise(resolve => setTimeout(resolve, 1000)); return fetchWithRetry(url, retries - 1); } }
2️⃣ 参考 2
JavaScript 异步编程全面解析
JavaScript 作为单线程语言,通过异步机制实现非阻塞操作。以下是核心概念和解决方案的演进:
异步编程的必要性
- 避免阻塞主线程(UI 冻结)
- 高效处理 I/O 操作(网络请求、文件读写)
- 实现动画、实时交互等场景
异步解决方案演进
1. 回调函数(Callback)
// 回调地狱示例
getData(a => {
processA(a, b => {
processB(b, c => {
processC(c) // 嵌套层级失控
})
})
})
痛点:回调地狱、错误处理困难
2. Promise(ES6)
getData()
.then(processA)
.then(processB)
.then(processC)
.catch(handleError) // 统一错误处理
优势:链式调用、状态管理
3. async/await(ES2017)
async function workflow() {
try {
const a = await getData()
const b = await processA(a)
const c = await processB(b)
return processC(c)
} catch (error) {
handleError(error)
}
}
优势:同步写法、try/catch 错误处理
核心机制:事件循环(Event Loop)
执行顺序规则:
- 执行所有同步代码(调用栈)
- 执行微任务队列(Microtask Queue):
- Promise 回调(then/catch/finally)
- queueMicrotask()
- MutationObserver
- 执行宏任务队列(Macrotask Queue):
- setTimeout/setInterval
- I/O 操作
- UI 渲染
- 事件回调
console.log("1. 同步");
setTimeout(() => console.log("4. 宏任务"), 0);
Promise.resolve()
.then(() => console.log("3. 微任务"));
console.log("2. 同步");
// 输出:1 → 2 → 3 → 4
关键异步模式
1. 并行执行
// Promise.all:全部成功才算成功
const [user, posts] = await Promise.all([
fetchUser(),
fetchPosts()
]);
// Promise.allSettled:获取所有结果状态
const results = await Promise.allSettled([
fetchA(),
fetchB()
]);
2. 竞速执行
// 第一个成功/失败的结果
const winner = await Promise.race([
fetchFast(),
fetchSlow()
]);
// 第一个成功的请求
const data = await Promise.any([
backupAPI1(),
backupAPI2()
]);
3. 错误重试
async function retry(fn, retries = 3) {
try {
return await fn();
} catch (err) {
if (retries <= 0) throw err;
await delay(1000);
return retry(fn, retries - 1);
}
}
异步最佳实践
-
避免阻塞主线程:
// 将耗时任务拆解 function chunkProcess() { doPart(); setTimeout(chunkProcess, 0); } -
优化 async/await:
// 并行替代顺序等待 const [a, b] = await Promise.all([getA(), getB()]); // 提前启动请求 const dataPromise = fetchData(); // 不阻塞 processSync(); const data = await dataPromise; -
优雅取消异步:
const controller = new AbortController(); fetch(url, { signal: controller.signal }) .catch(err => { if (err.name === 'AbortError') return; }); // 需要时取消 controller.abort(); -
微任务优化:
// 批量更新 UI function batchUpdate() { queueMicrotask(() => { // 在此更新 DOM }); }
常见陷阱及解决
-
循环中的 await:
// ❌ 顺序执行(慢) for (const url of urls) { await fetch(url); } // ✅ 并行执行 await Promise.all(urls.map(url => fetch(url))); -
未处理的拒绝:
// 添加全局捕获 process.on('unhandledRejection', err => { logError(err); }); -
上下文丢失:
// ❌ this 丢失 user.getData().then(function(data) { this.render(data); // this ≠ user }); // ✅ 箭头函数保留 this user.getData().then(data => this.render(data));
现代异步工具
- 顶层 await(ES2022):模块中直接使用 await
- 异步迭代:
for await...of处理流式数据 - Web Workers:CPU 密集型任务并行处理
- Atomics.waitAsync:高级并发控制
掌握 JavaScript 异步编程,能让你构建高性能、响应式的应用,同时避免回调地狱和性能瓶颈!
3️⃣ 参考 3
JavaScript 异步编程全面解析
JavaScript 是单线程语言,异步编程是其核心特性,用于处理非阻塞操作(如网络请求、文件读写等)。以下是完整的异步解决方案演进:
一、异步编程演进史
- 回调函数 (Callback):最基础的异步模式
- Promise:ES6 引入,解决"回调地狱"
- async/await:ES2017 引入,使异步代码像同步代码
二、核心机制:事件循环 (Event Loop) JavaScript 通过事件循环处理异步操作:
console.log('Start'); // 同步任务
setTimeout(() => {
console.log('Timeout'); // 异步宏任务
}, 0);
Promise.resolve().then(() => {
console.log('Promise'); // 异步微任务
});
console.log('End'); // 同步任务
/* 输出顺序:
Start
End
Promise (微任务优先)
Timeout (宏任务)
*/
三、异步解决方案详解
1. 回调函数 (Callback)
function fetchData(callback) {
setTimeout(() => {
callback('Data received');
}, 1000);
}
fetchData(data => {
console.log(data); // 1秒后输出 "Data received"
});
问题:回调地狱 (Callback Hell)
getUser(userId, user => {
getOrders(user.id, orders => {
getProducts(orders[0].id, product => {
// 嵌套层级越来越深...
});
});
});
2. Promise (ES6)
const promise = new Promise((resolve, reject) => {
// 异步操作
if (success) {
resolve(value); // 成功状态
} else {
reject(error); // 失败状态
}
});
// 使用示例
fetch('/api/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error))
.finally(() => console.log('Cleanup'));
3. async/await (ES2017)
async function fetchUser() {
try {
const response = await fetch('/api/user');
const data = await response.json();
return data;
} catch (error) {
console.error('Fetch failed:', error);
throw error; // 重新抛出错误
} finally {
console.log('Request completed');
}
}
// 使用
(async () => {
const user = await fetchUser();
console.log('User:', user);
})();
四、Promise 高级方法
| 方法 | 描述 | 使用场景 |
|---|---|---|
Promise.all() | 所有成功才成功,任一失败立即失败 | 并行独立操作 |
Promise.allSettled() | 等待所有 Promise 完成 | 需要知道所有结果 |
Promise.race() | 采用第一个完成的结果 | 请求超时处理 |
Promise.any() | 采用第一个成功的结果 | 多源数据获取 |
// 超时控制示例
const fetchWithTimeout = (url, timeout) => {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), timeout)
)
]);
};
五、异步最佳实践
-
避免嵌套层级
// 不良实践 getUser().then(user => { getOrders(user.id).then(orders => { // ... }); }); // 最佳实践 getUser() .then(user => getOrders(user.id)) .then(orders => { /* ... */ }); -
并行优化
// 顺序执行 (慢) async function sequential() { const a = await task1(); const b = await task2(); return [a, b]; } // 并行执行 (快) async function parallel() { const [a, b] = await Promise.all([task1(), task2()]); return [a, b]; } -
错误处理
// 集中错误处理 async function main() { try { const user = await fetchUser(); const orders = await fetchOrders(user.id); // ... } catch (error) { // 统一处理所有错误 logError(error); showUserMessage('操作失败'); } }
六、现代异步模式
-
顶层 await (ES2022)
// 模块中直接使用 const data = await fetchData(); console.log(data); -
异步迭代器 (for-await-of)
async function processStream(stream) { for await (const chunk of stream) { console.log('Received chunk:', chunk); processChunk(chunk); } } -
Web Workers 多线程
// 主线程 const worker = new Worker('task.js'); worker.postMessage(data); worker.onmessage = e => console.log(e.data); // task.js self.onmessage = e => { const result = heavyComputation(e.data); self.postMessage(result); };
七、异步调试技巧
-
Async Stack Traces
// Chrome DevTools 支持异步调用栈追踪 async function foo() { await bar(); } async function bar() { await baz(); // 错误时会显示完整调用链 } -
console.async
console.log('Start'); await new Promise(r => setTimeout(r, 100)); console.log('End'); // 控制台会显示异步时间线
总结:选择指南
| 场景 | 推荐方案 |
|---|---|
| 简单异步 | Promise.then |
| 复杂逻辑 | async/await |
| 并行请求 | Promise.all |
| 错误优先 | async/await + try/catch |
| 流处理 | for-await-of |
| CPU 密集型 | Web Workers |
掌握这些异步技术组合使用,可以构建高效、可维护的 JavaScript 应用程序。记住:
- 80% 场景使用
async/await - 并行操作使用
Promise.all - 始终处理错误(
.catch或try/catch) - 避免阻塞主线程
4️⃣ 参考 4
在 JavaScript 中,异步编程是处理耗时操作(如网络请求、文件读写、定时器等)的核心机制,它允许代码在等待操作完成时继续执行,避免阻塞主线程。以下是 JavaScript 异步编程的核心概念和技术:
1. 回调函数(Callbacks) 最早的异步处理方式,将函数作为参数传递给异步操作,完成后调用该函数。
setTimeout(() => {
console.log("延时 1 秒后执行");
}, 1000);
缺点:嵌套过深会导致“回调地狱”(Callback Hell),代码难以维护。
2. Promise
ES6 引入,用于管理异步操作的状态(pending/fulfilled/rejected)。
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("数据获取成功");
// 失败时:reject(new Error("请求失败"));
}, 1000);
});
};
fetchData()
.then(data => console.log(data))
.catch(error => console.error(error));
优点:链式调用(.then().catch())解决回调地狱。
3. Async/Await ES2017 语法糖,基于 Promise,用同步写法实现异步操作。
async function loadData() {
try {
const data = await fetchData(); // 等待 Promise 完成
console.log(data);
} catch (error) {
console.error(error);
}
}
loadData();
优点:代码更简洁,错误处理更直观(try/catch)。
4. Event Loop(事件循环) JavaScript 单线程运行的核心机制,通过循环处理任务队列:
- 调用栈(Call Stack):执行同步任务。
- 任务队列(Task Queue):
- 宏任务(Macrotasks):
setTimeout、setInterval、I/O。 - 微任务(Microtasks):
Promise.then、queueMicrotask、MutationObserver。
- 宏任务(Macrotasks):
- 执行顺序:
- 同步代码 → 所有微任务 → 一个宏任务 → 重复。
console.log("Start");
setTimeout(() => console.log("Timeout"), 0);
Promise.resolve()
.then(() => console.log("Promise 1"))
.then(() => console.log("Promise 2"));
console.log("End");
// 输出顺序:
// Start → End → Promise 1 → Promise 2 → Timeout
5. 常见异步场景
- 网络请求:
fetchAPI(返回 Promise)async function fetchUser() { const response = await fetch("https://api.example.com/user"); const user = await response.json(); console.log(user); } - 定时操作:
setTimeout/setInterval - 文件操作:Node.js 中的
fs.readFileconst fs = require("fs/promises"); fs.readFile("file.txt", "utf8").then(data => console.log(data));
6. 错误处理
- Promise:使用
.catch()捕获异常。 - Async/Await:用
try/catch包裹。 - 全局错误:
window.onerror(浏览器)或process.on('unhandledRejection')(Node.js)。
7. 进阶工具
Promise.all:并行执行多个 Promise,全部成功时返回结果数组。const [user, posts] = await Promise.all([fetchUser(), fetchPosts()]);Promise.race:返回最先完成的 Promise 结果。Promise.allSettled:等待所有 Promise 完成(无论成功/失败)。
关键总结
| 技术 | 特点 |
|---|---|
| 回调函数 | 基础但易嵌套过深,可读性差。 |
| Promise | 链式调用解决回调地狱,状态不可逆。 |
| Async/Await | 同步化写法,需搭配 try/catch 捕获错误。 |
| Event Loop | 理解微任务/宏任务的执行顺序是掌握异步的核心。 |
掌握这些概念后,可高效处理 JavaScript 中的异步操作,编写出高性能、易维护的代码。
5️⃣ 参考 5
JavaScript 异步编程全面指南
在 JavaScript 中,异步编程是处理非阻塞操作的核心机制。本文将系统介绍 JavaScript 异步编程的各种技术和最佳实践。
异步编程基础
为什么需要异步? JavaScript 是单线程语言,异步编程可以:
- 避免阻塞主线程,保持 UI 响应
- 高效处理 I/O 密集型操作(网络请求、文件读写)
- 管理定时任务和事件处理
事件循环机制 JavaScript 通过事件循环处理异步操作:
graph LR
A[调用栈] --> B{栈空?}
B -->|是| C[事件队列]
C --> D[取第一个任务]
D --> E[执行任务]
E --> B
异步编程技术演进
1. 回调函数(Callback) 最早的异步处理方式:
function fetchData(callback) {
setTimeout(() => {
callback('Data received');
}, 1000);
}
fetchData((data) => {
console.log(data); // 1秒后输出 "Data received"
});
问题:回调地狱
getUser(userId, (user) => {
getProfile(user.id, (profile) => {
getPosts(profile.id, (posts) => {
renderPosts(posts, () => {
// 更多嵌套...
});
});
});
});
2. Promise(ES6) Promise 提供了更结构化的异步处理:
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
Math.random() > 0.5 ?
resolve('Success!') :
reject('Error occurred');
}, 1000);
});
};
fetchData()
.then(data => console.log(data))
.catch(error => console.error(error))
.finally(() => console.log('Operation completed'));
3. async/await(ES2017) 基于 Promise 的语法糖,使异步代码更像同步代码:
async function fetchUserData() {
try {
const user = await getUser(userId);
const profile = await getProfile(user.id);
const posts = await getPosts(profile.id);
return posts;
} catch (error) {
console.error('Failed to fetch data:', error);
throw error;
} finally {
console.log('Data fetch completed');
}
}
Promise 核心方法
1. Promise.all 并行执行多个 Promise,全部成功时返回结果数组:
const loadAllData = async () => {
try {
const [users, posts, comments] = await Promise.all([
fetch('/api/users'),
fetch('/api/posts'),
fetch('/api/comments')
]);
console.log('All data loaded');
} catch (error) {
console.log('One request failed', error);
}
};
2. Promise.race 返回最先完成的 Promise:
const timeout = new Promise((_, reject) =>
setTimeout(() => reject('Request timed out'), 5000)
);
const fetchWithTimeout = () =>
Promise.race([
fetch('/api/data'),
timeout
]);
3. Promise.allSettled 所有 Promise 完成后返回状态数组:
const promises = [
fetch('/api/1'),
fetch('/api/2'),
fetch('/api/3')
];
Promise.allSettled(promises)
.then(results => {
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Request ${index} succeeded`);
} else {
console.error(`Request ${index} failed:`, result.reason);
}
});
});
高级异步模式
1. 异步迭代器 处理流式数据或分页请求:
async function* asyncPaginator() {
let page = 1;
while (true) {
const response = await fetch(`/api/items?page=${page}`);
const data = await response.json();
if (!data.items.length) break;
yield data.items;
page++;
}
}
(async () => {
for await (const items of asyncPaginator()) {
items.forEach(item => console.log(item));
}
})();
2. 取消异步操作 使用 AbortController 取消请求:
const controller = new AbortController();
const fetchData = async () => {
try {
const response = await fetch('/api/data', {
signal: controller.signal
});
return response.json();
} catch (error) {
if (error.name === 'AbortError') {
console.log('Request was aborted');
} else {
throw error;
}
}
};
// 取消请求
document.getElementById('cancel-btn').addEventListener('click', () => {
controller.abort();
});
3. Web Workers 在后台线程执行 CPU 密集型任务:
// main.js
const worker = new Worker('worker.js');
worker.postMessage({ type: 'CALCULATE', data: largeArray });
worker.onmessage = (event) => {
console.log('Result:', event.data);
};
// worker.js
self.onmessage = (event) => {
if (event.data.type === 'CALCULATE') {
const result = heavyComputation(event.data.data);
self.postMessage(result);
}
};
function heavyComputation(data) {
// 复杂计算...
return computedResult;
}
异步编程最佳实践
- 避免嵌套过深:使用 async/await 代替多层 then 链
- 并行独立操作:使用 Promise.all 提高效率
- 错误处理:始终使用 try/catch 或 catch 处理错误
- 资源清理:使用 finally 或 try/finally 确保资源释放
- 取消机制:为长时间操作实现取消功能
- 节流控制:使用库如 p-limit 控制并发请求数
- 超时处理:为不确定的操作添加超时机制
常见异步陷阱及解决方案
陷阱1:在循环中错误使用 await
// 错误:顺序执行导致性能问题
async function processItems(items) {
for (const item of items) {
await process(item); // 每个操作等待前一个完成
}
}
// 正确:并行处理
async function processItems(items) {
await Promise.all(items.map(item => process(item)));
}
陷阱2:忘记处理 Promise 拒绝
// 错误:未处理的 Promise 拒绝
async function riskyOperation() {
const result = await fetchUnreliableApi();
return result;
}
// 正确:添加错误处理
async function safeOperation() {
try {
const result = await fetchUnreliableApi();
return result;
} catch (error) {
console.error('Operation failed:', error);
return fallbackValue;
}
}
陷阱3:误解 async 函数返回值
async function getValue() {
return 42; // 实际返回 Promise.resolve(42)
}
// 正确使用
getValue().then(value => console.log(value)); // 42
// 错误:直接当作同步值使用
const value = getValue(); // value 是 Promise 对象
console.log(value); // Promise {<fulfilled>: 42}
总结
JavaScript 异步编程经历了从回调函数到 Promise 再到 async/await 的演进,提供了越来越强大的工具来处理异步操作。关键要点包括:
- 理解事件循环是掌握异步编程的基础
- Promise 是核心,async/await 是其语法糖
- 组合使用 Promise.all/race/allSettled 处理复杂场景
- 错误处理至关重要,避免未处理的拒绝
- 高级模式如异步迭代器和取消机制解决特定问题
- Web Workers 处理 CPU 密集型任务
随着 JavaScript 的发展,异步编程变得更加简洁和强大,合理运用这些技术可以编写出高效、可维护的异步代码。
6️⃣ 参考 6
JavaScript 异步编程全面指南
一、异步编程基础
1. 为什么需要异步编程?
- 单线程限制:JavaScript 是单线程语言,异步防止阻塞主线程
- 性能优化:避免等待 I/O 操作(网络请求、文件读写等)
- 用户体验:保持界面响应流畅
- 资源效率:充分利用 CPU 和 I/O 资源
2. 同步 vs 异步 示例
// 同步 - 阻塞执行
console.log("开始同步任务");
const result = networkRequestSync(); // 阻塞后续代码
console.log(result);
console.log("结束同步任务");
// 异步 - 非阻塞
console.log("开始异步任务");
networkRequestAsync(result => {
console.log(result);
});
console.log("结束异步任务"); // 立即执行
二、异步演进史
| 时期 | 技术 | 特点 |
|---|---|---|
| ES5 及之前 | 回调函数 | 简单但容易导致回调地狱 |
| ES6 (2015) | Promise | 链式调用,更好的错误处理 |
| ES7 (2016) | async/await | 同步风格编写异步代码 |
| ES2018 | 异步迭代器 | for-await-of 循环 |
| 现代浏览器 | Web Workers | 真正的并行执行 |
三、回调函数(Callback)
基本模式
function fetchData(callback) {
setTimeout(() => {
callback('数据加载完成');
}, 1000);
}
fetchData(result => {
console.log(result);
});
回调地狱问题
getUser(userId, user => {
getProfile(user.id, profile => {
getPosts(profile.id, posts => {
renderPosts(posts, () => {
notifyUser(() => {
// 更多嵌套...
});
});
});
});
});
解决方案
- 命名函数:避免匿名函数嵌套
function handlePosts(posts) { renderPosts(posts); } function handleProfile(profile) { getPosts(profile.id, handlePosts); } - 模块化:拆分功能为独立模块
- Promise:更先进的异步管理
四、Promise 核心机制
Promise 状态机
stateDiagram-v2
[*] --> Pending
Pending --> Fulfilled: resolve()
Pending --> Rejected: reject()
Fulfilled --> [*]
Rejected --> [*]
创建 Promise
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
Math.random() > 0.5 ?
resolve('成功') :
reject(new Error('失败'));
}, 1000);
});
链式方法对比
| 方法 | 作用 | 返回值 | 使用场景 |
|---|---|---|---|
.then() | 处理成功/失败 | 新 Promise | 主要操作流程 |
.catch() | 捕获错误 | 新 Promise | 全局错误处理 |
.finally() | 最终清理 | 新 Promise | 资源清理,状态无关操作 |
五、async/await 革命
基本用法
async function fetchUserData() {
try {
const user = await getUser();
const posts = await getPosts(user.id);
return { user, posts };
} catch (error) {
console.error("获取数据失败:", error);
return null;
}
}
执行流程解析
sequenceDiagram
participant Main as 主线程
participant Async as async函数
participant Promise
Main->>Async: 调用异步函数
Async->>Async: 执行同步代码
Async->>Promise: await 暂停执行
Main->>Main: 继续执行其他代码
Promise-->>Async: Promise 解决
Async->>Async: 恢复执行
Async->>Main: 返回结果
六、高级异步模式
1. 并行执行
// 顺序执行 (慢)
const user = await getUser();
const posts = await getPosts();
// 并行执行 (快)
const [user, posts] = await Promise.all([
getUser(),
getPosts()
]);
2. 竞态控制
// 获取最快响应的数据源
const data = await Promise.race([
fetchFromSourceA(),
fetchFromSourceB()
]);
// 带超时的请求
async function fetchWithTimeout(url, timeout) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('请求超时')), timeout)
)
]);
}
3. 异步迭代
async function processItems(items) {
for await (const item of asyncIterator(items)) {
await processItem(item);
}
}
4. 取消异步操作
function createCancellablePromise(promise) {
let cancel;
const wrapped = new Promise((resolve, reject) => {
cancel = reject;
promise.then(resolve).catch(reject);
});
return [wrapped, () => cancel(new Error('操作已取消'))];
}
// 使用
const [fetchPromise, cancel] = createCancellablePromise(fetchData());
cancelButton.addEventListener('click', cancel);
七、事件循环(Event Loop)详解
JavaScript 运行时结构
graph LR
HEAP[堆] -->|对象分配| ST[执行栈]
WEB[Web APIs] -->|回调| TQ[任务队列]
TQ --> EL[事件循环]
EL --> ST
执行顺序示例
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
// 输出顺序:1, 4, 3, 2
任务类型
| 任务类型 | 示例 | 优先级 |
|---|---|---|
| 微任务 | Promise 回调 | 高 |
| 宏任务 | setTimeout, setInterval | 低 |
| 渲染任务 | requestAnimationFrame | 中 |
八、实际应用场景
1. 数据获取与处理
async function loadDashboard() {
try {
const [user, stats, notifications] = await Promise.all([
fetchUser(),
fetchStats(),
fetchNotifications()
]);
return {
user: processUser(user),
stats: calculateMetrics(stats),
notifications: filterImportant(notifications)
};
} catch (error) {
showError(error);
return loadCachedDashboard();
}
}
2. 文件上传进度
async function uploadFile(file, onProgress) {
const formData = new FormData();
formData.append('file', file);
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', e => {
if (e.lengthComputable) {
onProgress(Math.round((e.loaded / e.total) * 100));
}
});
xhr.addEventListener('load', () => resolve(xhr.response));
xhr.addEventListener('error', () => reject(new Error('上传失败')));
xhr.open('POST', '/upload');
xhr.send(formData);
});
}
3. 用户交互流程
async function purchaseFlow() {
showLoading();
try {
const user = await verifyUser();
const payment = await processPayment(user);
const confirmation = await sendConfirmation(user, payment);
return showSuccess(confirmation);
} catch (error) {
showError(error.message);
return retryPurchase();
} finally {
hideLoading();
}
}
九、常见问题与解决方案
1. 回调地狱 → Promise链/async-await
// 回调地狱
getData(a => {
processA(a, b => {
processB(b, c => {
processC(c);
});
});
});
// 解决方案
async function process() {
const a = await getData();
const b = await processA(a);
const c = await processB(b);
return processC(c);
}
2. 未处理的拒绝 → 全局错误处理
// Node.js
process.on('unhandledRejection', (reason, promise) => {
console.error('未处理的拒绝:', reason);
});
// 浏览器
window.addEventListener('unhandledrejection', event => {
console.error('未处理的拒绝:', event.reason);
});
3. 并发控制
async function limitedConcurrency(tasks, limit) {
const results = [];
const executing = [];
for (const task of tasks) {
const p = task().then(result => {
executing.splice(executing.indexOf(p), 1);
return result;
});
results.push(p);
executing.push(p);
if (executing.length >= limit) {
await Promise.race(executing);
}
}
return Promise.all(results);
}
十、现代异步 API
1. Fetch API
async function fetchData(url) {
try {
const response = await fetch(url);
if (!response.ok) throw new Error('网络响应异常');
return response.json();
} catch (error) {
console.error('请求失败:', error);
return null;
}
}
2. Web Workers
// 主线程
const worker = new Worker('worker.js');
worker.postMessage({ data: 'heavy computation' });
worker.onmessage = e => console.log('结果:', e.data);
// worker.js
self.onmessage = e => {
const result = compute(e.data);
self.postMessage(result);
};
3. Async Clipboard API
async function copyText(text) {
try {
await navigator.clipboard.writeText(text);
console.log('复制成功');
} catch (err) {
console.error('复制失败:', err);
}
}
十一、最佳实践
- 优先 async/await:提高代码可读性
- 始终处理错误:使用 try/catch 或 .catch()
- 避免阻塞主线程:将重型计算移到 Web Worker
- 合理使用并行:Promise.all 优化独立操作
- 谨慎使用第三方库:了解其异步实现方式
- 监控未处理拒绝:设置全局错误处理器
十二、调试技巧
-
异步堆栈追踪:
async function main() { await step1(); } async function step1() { await step2(); } async function step2() { throw new Error('调试信息'); } main().catch(console.error); -
console异步标记:
console.time('API请求'); const data = await fetchData(); console.timeEnd('API请求'); // 输出执行时间 -
浏览器调试工具:
- Async 调用栈视图
- Promise 状态检查器
- 网络面板的异步请求跟踪
总结
JavaScript 异步编程演进:
graph LR
A[回调函数] --> B[Promise]
B --> C[Generator]
C --> D[async/await]
D --> E[异步迭代器]
E --> F[Web Workers]
掌握 JavaScript 异步编程的关键:
- 理解事件循环机制 - 微任务 vs 宏任务
- 熟练 Promise 核心概念 - 状态、链式调用、组合
- 合理使用 async/await - 同步风格写异步代码
- 掌握高级模式 - 并发控制、取消操作、进度跟踪
- 遵循最佳实践 - 错误处理、性能优化、代码可维护性
通过深入理解这些概念和技术,你将能够构建高效、响应迅速的 JavaScript 应用程序,轻松处理从简单到复杂的各种异步场景。
7️⃣ 参考 7
JavaScript 异步编程是其核心特性之一,它允许代码在等待操作(如网络请求、文件 I/O)完成时继续执行,避免阻塞主线程。以下是 JavaScript 异步编程的全面解析:
一、异步编程的必要性 JavaScript 是单线程语言,浏览器中只有一个主线程负责:
- 渲染 UI
- 处理用户交互
- 执行 JavaScript 代码
如果所有操作都同步执行:
const data = fetchData(); // 假设耗时 2 秒
renderUI(); // 被阻塞 2 秒
handleClick(); // 用户点击无响应
异步编程解决了这个问题,让耗时操作在后台执行,完成后通过回调通知主线程。
二、异步演进史 1. 回调函数 (Callback) 最基础的异步模式:
function fetchData(callback) {
setTimeout(() => {
callback('数据加载完成');
}, 1000);
}
fetchData(result => {
console.log(result); // 1秒后输出
});
问题:回调地狱 (Callback Hell)
getUser(id, user => {
getPosts(user.id, posts => {
getComments(posts[0].id, comments => {
// 更多嵌套...
});
});
});
2. Promise (ES6) 状态机管理异步操作:
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
Math.random() > 0.5 ?
resolve('成功') :
reject('失败');
}, 1000);
});
}
fetchData()
.then(console.log)
.catch(console.error);
3. async/await (ES2017) 用同步写法处理异步:
async function loadData() {
try {
const data = await fetchData();
console.log(data);
} catch (error) {
console.error(error);
}
}
三、核心异步机制 1. 事件循环 (Event Loop) JavaScript 的异步基石:
graph LR
A[调用栈] --> B[Web APIs]
B --> C[任务队列]
C --> D[事件循环]
D --> A
- 调用栈:同步代码执行
- Web APIs:处理定时器、网络请求等
- 任务队列:存储回调(微任务/宏任务)
- 事件循环:检查调用栈为空时执行队列任务
2. 任务类型
| 类型 | 示例 | 优先级 |
|---|---|---|
| 微任务 | Promise.then、queueMicrotask | 高 |
| 宏任务 | setTimeout、setInterval | 低 |
执行顺序:同步代码 > 微任务 > 宏任务
console.log('1'); // 同步
setTimeout(() => console.log('2'), 0); // 宏任务
Promise.resolve().then(() => {
console.log('3'); // 微任务
});
console.log('4'); // 同步
// 输出:1 → 4 → 3 → 2
四、现代异步模式 1. Promise 组合方法
// 并行执行
Promise.all([fetchA(), fetchB()])
.then(([a, b]) => console.log(a, b));
// 任意一个完成
Promise.race([fetchA(), fetchB()])
.then(first => console.log(first));
// 全部完成(无论成功失败)
Promise.allSettled([fetchA(), fetchB()])
.then(results => results.forEach(console.log));
// 任意一个成功
Promise.any([fetchA(), fetchB()])
.then(firstSuccess => console.log(firstSuccess));
2. 异步迭代 处理流式数据:
async function processStream() {
const asyncIterable = createAsyncIterable();
for await (const chunk of asyncIterable) {
console.log('收到数据块:', chunk);
}
}
3. Web Workers 多线程并行计算:
// 主线程
const worker = new Worker('worker.js');
worker.postMessage({ data: heavyData });
worker.onmessage = e => console.log(e.data);
// worker.js
self.onmessage = e => {
const result = heavyComputation(e.data);
self.postMessage(result);
};
五、异步最佳实践 1. 错误处理
// 正确方式:集中捕获
async function main() {
try {
await operation1();
await operation2();
} catch (error) {
console.error('全局捕获:', error);
}
}
// 危险:部分错误被忽略
async function risky() {
await operation1().catch(e => console.log(e)); // ❌ 错误1被吞
await operation2(); // 错误2未被处理
}
2. 性能优化
// 串行 vs 并行
async function serial() {
const a = await fetchA(); // 等待
const b = await fetchB(); // 等待
return [a, b]; // 总时间 = A+B
}
async function parallel() {
const [a, b] = await Promise.all([fetchA(), fetchB()]);
return [a, b]; // 总时间 = max(A,B)
}
3. 取消异步
使用 AbortController:
const controller = new AbortController();
async function fetchWithCancel() {
try {
const res = await fetch('/api', {
signal: controller.signal
});
return res.json();
} catch (err) {
if (err.name === 'AbortError') {
console.log('请求被取消');
}
}
}
// 取消请求
controller.abort();
六、常见异步场景 1. 网络请求
async function loadUser() {
const response = await fetch('/api/user');
if (!response.ok) throw new Error('请求失败');
return response.json();
}
2. 定时操作
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function animate() {
await delay(1000);
element.style.opacity = '0.5';
}
3. 文件读取
async function readFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsText(file);
});
}
七、调试技巧
-
异步堆栈追踪:
async function parent() { await child(); // 保留完整调用栈 } -
console 增强:
console.time('load'); await loadData(); console.timeEnd('load'); // 输出执行时间 -
Promise 可视化: 使用浏览器开发者工具的 Promise 调试功能
八、异步编程原则
- 避免阻塞主线程 - 任何超过 50ms 的操作应异步化
- 优先使用 async/await - 提高代码可读性
- 合理使用并行 - 用
Promise.all加速独立操作 - 明确错误处理 - 所有异步路径都应有错误处理
- 考虑取消机制 - 对可能中断的操作提供取消支持
总结:JavaScript 异步工具箱
| 工具 | 适用场景 | 关键优势 |
|---|---|---|
| 回调函数 | 简单异步操作、事件处理 | 浏览器兼容性好 |
| Promise | 链式异步操作、错误传播 | 解决回调地狱 |
| async/await | 复杂异步逻辑、同步风格代码 | 代码可读性高 |
| Web Workers | CPU 密集型任务 | 多线程并行 |
| RxJS | 事件流处理、高级异步组合 | 响应式编程能力强大 |
掌握这些异步技术,可以构建高性能、响应迅速的 JavaScript 应用,有效处理 I/O 操作、网络请求等场景,同时保持流畅的用户体验。
8️⃣ 参考 8
JavaScript 异步编程完全指南:从回调到 async/await
一、JavaScript 异步编程演进史
graph LR
A[回调函数] --> B[Promise]
B --> C[Generator]
C --> D[async/await]
D --> E[顶层await]
二、核心异步模式详解
1. 回调函数(Callback)
function fetchData(callback) {
setTimeout(() => {
const data = { id: 1, name: '示例数据' };
callback(null, data); // 错误优先模式
}, 1000);
}
// 使用
fetchData((err, data) => {
if (err) console.error(err);
else console.log(data);
});
问题:回调地狱(Callback Hell)
getUser(userId, (err, user) => {
if (err) return handleError(err);
getPosts(user.id, (err, posts) => {
if (err) return handleError(err);
getComments(posts[0].id, (err, comments) => {
if (err) return handleError(err);
// 更多嵌套...
});
});
});
2. Promise(承诺)
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
Math.random() > 0.5 ?
resolve('成功数据') :
reject(new Error('失败原因'));
}, 1000);
});
// 使用
promise
.then(data => console.log(data))
.catch(error => console.error(error));
3. async/await(异步/等待)
async function fetchUserData() {
try {
const response = await fetch('/api/user');
const user = await response.json();
const posts = await fetch(`/api/posts?userId=${user.id}`);
return { user, posts };
} catch (error) {
console.error('请求失败:', error);
return null;
} finally {
console.log('请求完成');
}
}
三、Promise 高级用法
1. 组合多个 Promise
| 方法 | 描述 | 示例 |
|---|---|---|
Promise.all() | 所有成功或一个失败 | Promise.all([p1, p2]) |
Promise.race() | 第一个解决或拒绝 | Promise.race([p1, p2]) |
Promise.allSettled() | 所有解决或拒绝 | Promise.allSettled([p1, p2]) |
Promise.any() | 第一个成功解决 | Promise.any([p1, p2]) |
2. 超时控制实现
function timeout(promise, ms) {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error('超时')), ms)
)
]);
}
// 使用
async function fetchWithTimeout() {
try {
const response = await timeout(fetch('/api/data'), 5000);
return response.json();
} catch (error) {
console.error(error.message); // "超时"
}
}
3. 自动重试机制
async function retry(fn, maxAttempts = 3, delay = 1000) {
try {
return await fn();
} catch (error) {
if (maxAttempts <= 1) throw error;
await new Promise(r => setTimeout(r, delay));
return retry(fn, maxAttempts - 1, delay * 2); // 指数退避
}
}
// 使用
async function fetchResource() {
return retry(() => fetch('/api/unstable'), 5);
}
四、async/await 最佳实践
1. 并行优化技巧
// 低效:顺序等待
async function slow() {
const a = await fetchA();
const b = await fetchB(); // 等待A完成
return { a, b };
}
// 高效:并行执行
async function fast() {
const [a, b] = await Promise.all([fetchA(), fetchB()]);
return { a, b };
}
2. 循环中的异步处理
// 顺序执行
async function processSequentially(items) {
for (const item of items) {
await processItem(item);
}
}
// 并行执行
async function processInParallel(items) {
await Promise.all(items.map(item => processItem(item)));
}
// 控制并发数
async function processWithConcurrency(items, concurrency = 5) {
const results = [];
for (let i = 0; i < items.length; i += concurrency) {
const chunk = items.slice(i, i + concurrency);
const chunkResults = await Promise.all(chunk.map(processItem));
results.push(...chunkResults);
}
return results;
}
3. 错误处理模式
// 方式1:try/catch
async function fetchData() {
try {
const data = await fetch('/api');
return process(data);
} catch (error) {
console.error(error);
return fallback();
}
}
// 方式2:catch方法
async function loadResource() {
const data = await fetch('/api')
.catch(error => {
console.warn('使用缓存数据:', error);
return getCachedData();
});
return process(data);
}
// 方式3:错误包装
async function safeFetch() {
const [error, data] = await fetch('/api')
.then(data => [null, data])
.catch(error => [error, null]);
if (error) return fallback();
return data;
}
五、现代异步模式
1. 顶层 await (ES2022)
// 模块顶层直接使用await
const config = await fetchConfig();
export default initializeApp(config);
2. Promise.withResolvers (ES2024)
// 创建可外部控制的Promise
const { promise, resolve, reject } = Promise.withResolvers();
document.getElementById('btn').addEventListener('click', () => {
resolve('按钮被点击');
});
const result = await promise;
console.log(result); // "按钮被点击"
3. 异步迭代器 for-await-of
async function processStream() {
const stream = getReadableStream(); // 如Node.js流或异步生成器
for await (const chunk of stream) {
console.log('收到数据块:', chunk);
await processChunk(chunk);
}
}
六、常见异步场景解决方案
1. 用户交互序列
async function userRegistration() {
try {
const email = await showEmailModal();
const password = await showPasswordModal();
const user = await createUser(email, password);
await sendWelcomeEmail(user);
return user;
} catch (error) {
if (error instanceof UserCancelError) {
console.log('用户取消注册');
} else {
showError(error.message);
}
}
}
2. API 请求瀑布流
async function fetchUserDashboard(userId) {
const user = await fetchUser(userId);
const [orders, messages] = await Promise.all([
fetchOrders(user.lastOrderId),
fetchMessages(user.inboxId)
]);
const recommendations = await fetchRecommendations(user.interests);
return { user, orders, messages, recommendations };
}
3. 实时数据轮询
async function pollData(url, interval = 5000) {
while (true) {
try {
const response = await fetch(url);
const data = await response.json();
if (data.status === 'ready') return data;
if (data.status === 'failed') throw new Error('处理失败');
await new Promise(r => setTimeout(r, interval));
} catch (error) {
console.error('轮询错误:', error);
await new Promise(r => setTimeout(r, interval * 2)); // 错误时增加间隔
}
}
}
七、性能优化与陷阱规避
1. 内存泄漏预防
async function processLargeDataset() {
const stream = getDataStream();
let processed = 0;
for await (const chunk of stream) {
const result = await processChunk(chunk);
await saveResult(result);
processed++;
if (processed % 1000 === 0) {
// 定期释放内存
await new Promise(resolve => setTimeout(resolve, 0));
}
}
}
2. 避免阻塞事件循环
// 将CPU密集型任务拆解
async function processLargeArray(array) {
const results = [];
const CHUNK_SIZE = 1000;
for (let i = 0; i < array.length; i += CHUNK_SIZE) {
const chunk = array.slice(i, i + CHUNK_SIZE);
// 允许事件循环处理其他任务
await new Promise(resolve => setTimeout(resolve, 0));
results.push(...await processChunk(chunk));
}
return results;
}
3. 常见陷阱解决方案
| 陷阱 | 错误示例 | 解决方案 |
|---|---|---|
| 忘记await | saveData(data); | await saveData(data); |
| 循环中错误处理 | array.forEach(async item => {...}) | 使用for...of循环 |
| 并行意外顺序化 | await p1; await p2; | Promise.all([p1, p2]) |
| Promise未处理拒绝 | fetch('/api'); | 添加.catch()处理 |
| 资源未释放 | 无finally块 | 使用try/finally |
八、异步调试技巧
1. 异步堆栈追踪
// 启用异步堆栈追踪
async function fetchData() {
// 在错误对象中保留原始堆栈
const error = new Error('原始错误');
Error.captureStackTrace(error, fetchData);
await new Promise(r => setTimeout(r, 100));
throw error;
}
// 使用
fetchData().catch(err => console.log(err.stack));
2. 使用async_hooks (Node.js)
const async_hooks = require('async_hooks');
// 创建异步上下文跟踪
const ctx = new Map();
const hook = async_hooks.createHook({
init(asyncId, type, triggerAsyncId) {
ctx.set(asyncId, { type, triggerAsyncId });
},
destroy(asyncId) {
ctx.delete(asyncId);
}
});
hook.enable();
// 调试异步操作
async function main() {
await fetchData();
// 查看当前异步上下文
console.log(ctx.get(async_hooks.executionAsyncId()));
}
九、总结:JavaScript 异步编程全景
1. 技术选型指南
| 场景 | 推荐方案 |
|---|---|
| 简单异步操作 | Promise.then/catch |
| 复杂异步流程 | async/await |
| 并行独立请求 | Promise.all |
| 实时数据流 | 异步迭代器 for-await-of |
| 浏览器事件 | Promise + 事件监听 |
| Node.js 流处理 | pipeline + 异步迭代 |
2. 最佳实践清单
- 优先使用 async/await 提高可读性
- 并行独立操作 使用 Promise.all
- 始终处理错误 使用 try/catch 或 .catch()
- 清理资源 使用 finally 块
- 避免阻塞事件循环 拆分CPU密集型任务
- 使用现代语法 如顶层 await 和 Promise 组合方法
- 监控未处理拒绝 添加全局处理器:
process.on('unhandledRejection', (reason) => { console.error('未处理的Promise拒绝:', reason); });
3. 未来趋势
- Web Workers:将CPU密集型任务分流到后台线程
- WebAssembly:高性能计算与JavaScript互操作
- Reactive Programming:RxJS等库处理复杂异步事件流
- 并发原语:如SharedArrayBuffer和Atomics API
graph TD
A[异步问题] --> B{解决方案}
B --> C[回调函数]
B --> D[Promise]
B --> E[async/await]
B --> F[响应式编程]
C --> G[回调地狱]
D --> H[链式调用]
E --> I[同步风格]
F --> J[事件流处理]
H --> K[可读性提升]
I --> K
J --> K
K --> L[可维护应用]
掌握JavaScript异步编程是现代Web开发的必备技能。从基础的回调函数到现代的async/await,理解每种模式的适用场景和最佳实践,能够帮助你构建高性能、可维护的应用程序。随着ECMAScript标准的不断发展,JavaScript的异步处理能力也将继续增强。
9️⃣ 参考 9
JavaScript 异步编程全面指南
JavaScript 是单线程语言,异步编程是其核心特性之一,用于处理非阻塞操作。下面我将全面解析 JavaScript 的异步编程模型,涵盖从基础到高级的所有概念。
异步编程的核心概念
1. 事件循环(Event Loop) JavaScript 的异步机制基于事件循环:
console.log('开始'); // 同步任务
setTimeout(() => {
console.log('定时器回调'); // 宏任务
}, 0);
Promise.resolve().then(() => {
console.log('Promise 回调'); // 微任务
});
console.log('结束'); // 同步任务
// 输出顺序:
// 开始
// 结束
// Promise 回调
// 定时器回调
2. 回调函数(Callbacks) 最早的异步处理方式:
function fetchData(callback) {
setTimeout(() => {
callback('数据加载完成');
}, 1000);
}
fetchData((result) => {
console.log(result);
});
Promise 核心机制
1. Promise 基础
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
Math.random() > 0.5 ?
resolve('成功!') :
reject(new Error('失败!'));
}, 1000);
});
2. Promise 方法链
promise
.then(result => {
console.log(result);
return result.toUpperCase();
})
.then(uppercaseResult => {
console.log(uppercaseResult);
})
.catch(error => {
console.error('错误:', error.message);
})
.finally(() => {
console.log('操作完成');
});
3. Promise 静态方法
// 并行处理多个Promise
Promise.all([
fetch('/api/user'),
fetch('/api/posts')
])
.then(([user, posts]) => {
console.log('用户:', user);
console.log('文章:', posts);
})
.catch(error => {
console.error('有一个请求失败', error);
});
// 其他实用方法:
Promise.race([p1, p2]); // 最先完成的Promise
Promise.allSettled([p1, p2]); // 所有Promise完成(无论成功失败)
Promise.any([p1, p2]); // 第一个成功的Promise
Async/Await 现代异步处理
1. 基本用法
async function fetchUserData() {
try {
const response = await fetch('https://api.example.com/user');
const data = await response.json();
// 并行处理
const [orders, messages] = await Promise.all([
fetchOrders(data.id),
fetchMessages(data.id)
]);
return { user: data, orders, messages };
} catch (error) {
console.error('数据获取失败:', error);
throw error; // 重新抛出错误
} finally {
console.log('请求结束');
}
}
2. 高级模式 错误优先处理:
async function safeFetch(url) {
try {
const response = await fetch(url);
return [null, await response.json()];
} catch (error) {
return [error, null];
}
}
// 使用
const [err, data] = await safeFetch('/api/data');
异步迭代:
async function processItems(items) {
// 顺序处理
for (const item of items) {
await processItem(item);
}
// 并行处理(限制并发数)
const concurrencyLimit = 5;
for (let i = 0; i < items.length; i += concurrencyLimit) {
const batch = items.slice(i, i + concurrencyLimit);
await Promise.all(batch.map(processItem));
}
}
性能优化策略
1. 并行与顺序执行
// 错误:顺序执行(慢)
async function sequentialRequests() {
const user = await fetch('/user');
const posts = await fetch(`/posts/${user.id}`);
return posts;
}
// 正确:并行执行(快)
async function parallelRequests() {
const [user, posts] = await Promise.all([
fetch('/user'),
fetch('/posts') // 如果不需要user.id
]);
return { user, posts };
}
2. 取消异步操作
const controller = new AbortController();
async function fetchWithCancel(url) {
try {
const response = await fetch(url, {
signal: controller.signal
});
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
console.log('请求被取消');
} else {
throw error;
}
}
}
// 取消请求
controller.abort();
异步编程最佳实践
- 优先使用 async/await:提高代码可读性
- 始终处理错误:避免未捕获的 Promise 拒绝
- 避免过度嵌套:使用 Promise.all 或异步循环
- 注意内存泄漏:清理事件监听器和引用
- 使用异步队列:控制高并发场景
- 考虑使用 RxJS:复杂异步场景的响应式编程
- 监控异步性能:使用 Chrome DevTools 的 Async Stack Traces
现代异步模式
1. 顶层 await (ES2022) 在模块顶层直接使用 await:
// 模块文件
const data = await fetchData();
export default data;
2. Promise 组合
function withTimeout(promise, timeout) {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error('超时')), timeout)
)
]);
}
// 使用
try {
const result = await withTimeout(fetch('/api'), 5000);
} catch (error) {
console.error(error.message); // "超时" 或 API 错误
}
总结
JavaScript 异步编程已从回调地狱演进到现代的 async/await 模式。关键要点:
- 理解事件循环:微任务 vs 宏任务
- 掌握 Promise:基础、链式调用和静态方法
- 善用 async/await:编写同步风格的异步代码
- 优化性能:并行处理、取消机制
- 错误处理:try/catch 和 Promise.catch
graph TD
A[同步代码] --> B{异步操作}
B -->|成功| C[Promise.then / await]
B -->|失败| D[Promise.catch / try-catch]
C --> E[继续执行]
D --> E
E --> F[结束]
通过合理运用这些异步技术,您可以构建高性能、可维护的 JavaScript 应用程序。
🔟 参考 10
JavaScript 异步编程完全指南
核心机制:事件循环 (Event Loop) JavaScript 是单线程语言,异步行为通过事件循环实现:
console.log("Start");
setTimeout(() => console.log("Timeout"), 0);
Promise.resolve().then(() => console.log("Promise"));
console.log("End");
/* 输出顺序:
Start
End
Promise
Timeout
*/
执行顺序:
- 同步代码(宏任务)
- 所有微任务(Promise.then, queueMicrotask)
- 一个宏任务(setTimeout, setInterval, I/O)
- 重复步骤2-3
异步解决方案演进
1. 回调函数 (Callback)
function fetchData(callback) {
setTimeout(() => {
callback('Data received');
}, 1000);
}
fetchData(data => console.log(data));
问题:回调地狱 (Callback Hell)
getUser(id, user => {
getPosts(user, posts => {
getComments(posts[0], comments => {
// 嵌套层级过深...
});
});
});
2. Promise (ES6)
const fetchData = () => new Promise((resolve, reject) => {
setTimeout(() => {
Math.random() > 0.5 ?
resolve('Success') :
reject('Error');
}, 1000);
});
fetchData()
.then(data => console.log(data))
.catch(err => console.error(err))
.finally(() => console.log('Cleanup'));
3. async/await (ES2017)
async function loadData() {
try {
const data = await fetchData();
console.log(data);
} catch (err) {
console.error(err);
} finally {
console.log('Cleanup');
}
}
Promise 核心方法
| 方法 | 描述 | 示例 |
|---|---|---|
Promise.resolve() | 创建成功Promise | Promise.resolve(42) |
Promise.reject() | 创建失败Promise | Promise.reject('error') |
Promise.all() | 所有成功才算成功 | Promise.all([p1, p2]) |
Promise.any() | 任意成功即成功 | Promise.any([p1, p2]) |
Promise.race() | 第一个完成的结果 | Promise.race([p1, p2]) |
Promise.allSettled() | 所有完成(无论成败) | Promise.allSettled([p1, p2]) |
高级异步模式
1. 取消异步操作 (AbortController)
const controller = new AbortController();
fetch(url, { signal: controller.signal })
.then(response => response.json())
.catch(err => {
if (err.name === 'AbortError') {
console.log('Request aborted');
}
});
// 取消请求
controller.abort();
2. 异步迭代 (for-await-of)
async function processStream(stream) {
for await (const chunk of stream) {
console.log('Received chunk:', chunk);
}
}
3. 顶级 await (ES2022)
// 模块顶层使用
const data = await fetchData();
export default data;
4. 异步队列控制
class AsyncQueue {
constructor() {
this.queue = [];
this.processing = false;
}
async enqueue(task) {
this.queue.push(task);
if (!this.processing) await this.process();
}
async process() {
this.processing = true;
while (this.queue.length > 0) {
const task = this.queue.shift();
await task();
}
this.processing = false;
}
}
性能优化技巧
- 并行异步操作:
// 顺序执行 (慢)
const results = [];
for (const url of urls) {
results.push(await fetch(url));
}
// 并行执行 (快)
const promises = urls.map(url => fetch(url));
const results = await Promise.all(promises);
- 限制并发数:
async function parallelWithLimit(tasks, limit) {
const results = [];
const executing = new Set();
for (const task of tasks) {
const p = task();
executing.add(p);
p.then(() => executing.delete(p));
if (executing.size >= limit) {
await Promise.race(executing);
}
}
return Promise.allSettled(results);
}
- 缓存异步结果:
function createAsyncCache(fn) {
const cache = new Map();
return async (key) => {
if (cache.has(key)) return cache.get(key);
const result = await fn(key);
cache.set(key, result);
return result;
};
}
常见异步场景解决方案
1. 超时处理
async function fetchWithTimeout(url, timeout) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeoutId);
return response.json();
} catch (err) {
throw new Error(`Request timed out after ${timeout}ms`);
}
}
2. 重试机制
async function retry(fn, retries = 3, delay = 1000) {
try {
return await fn();
} catch (err) {
if (retries <= 0) throw err;
await new Promise(r => setTimeout(r, delay));
return retry(fn, retries - 1, delay * 2); // 指数退避
}
}
3. 批量处理
async function batchProcess(items, processFn, batchSize = 10) {
const results = [];
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
const batchResults = await Promise.allSettled(
batch.map(item => processFn(item))
);
results.push(...batchResults);
}
return results;
}
异步调试技巧
- 异步堆栈追踪:
// 启用 V8 异步堆栈追踪
// Chrome DevTools -> Settings -> Experiments -> 勾选 "Async stack traces"
- console 异步标记:
console.time('API Call');
const data = await fetchData();
console.timeEnd('API Call'); // 输出执行时间
- Promise 可视化: 使用浏览器 Performance 面板记录 Promise 创建和解决时间
现代异步 API 整合
| API | 描述 | 异步处理 |
|---|---|---|
| Fetch API | 网络请求 | await fetch() |
| File System | 文件系统 (Node.js) | fs.promises.readFile() |
| Web Workers | 多线程 | worker.postMessage() + onmessage |
| WebSockets | 实时通信 | socket.onmessage |
| IndexedDB | 客户端数据库 | db.transaction() |
// Fetch + async/await 示例
async function getData() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) throw new Error('Network response was not ok');
return await response.json();
} catch (error) {
console.error('Fetch error:', error);
throw error;
}
}
最佳实践
- 避免混合使用 then/catch 和 async/await
- 始终处理 Promise 拒绝(使用 catch 或 try/catch)
- 合理使用 Promise 组合方法(all/any/race)
- 异步代码性能监控(特别是循环中的 await)
- 优先使用原生异步 API(而非回调版本)
- 复杂异步流程使用状态机管理
- 浏览器环境考虑兼容性(async/await 需要 ES2017+)
JavaScript 异步编程已从回调地狱演进到现代的 async/await 模式。理解事件循环机制、掌握 Promise 核心方法、合理运用 async/await 语法,配合现代异步 API,可以编写出高效可维护的异步代码。
🤔 参考 11
JavaScript 异步编程全面解析
JavaScript 的异步编程是其核心特性之一,允许执行长时间操作而不阻塞主线程。以下是完整的异步编程知识体系:
异步编程演进史
- 回调函数 (Callback):最基础的异步模式
- Promise (ES6):更结构化的异步处理
- async/await (ES2017):使异步代码像同步代码
- 异步迭代器 (ES2018):
for-await-of循环
一、回调函数(Callback) 最原始的异步处理方式,但容易导致"回调地狱"
function fetchData(callback) {
setTimeout(() => {
callback('数据加载完成');
}, 1000);
}
fetchData((result) => {
console.log(result);
});
问题:嵌套回调导致代码难以维护(回调地狱)
getUser(id, (user) => {
getProfile(user, (profile) => {
getPosts(profile, (posts) => {
// 更多嵌套...
});
});
});
二、Promise(ES6 核心解决方案) 基本结构
const promise = new Promise((resolve, reject) => {
// 异步操作
if (success) {
resolve(value); // 成功状态
} else {
reject(error); // 失败状态
}
});
Promise 链式操作
fetch('/api/data')
.then(response => response.json())
.then(data => processData(data))
.then(result => {
console.log('处理结果:', result);
})
.catch(error => {
console.error('处理失败:', error);
})
.finally(() => {
console.log('请求结束');
});
静态方法
| 方法 | 描述 |
|---|---|
Promise.all() | 所有成功时返回结果数组 |
Promise.any() | 任意一个成功即返回 |
Promise.race() | 第一个完成(无论成功失败) |
Promise.allSettled() | 所有完成后返回结果状态 |
三、async/await(ES2017 语法糖) 使异步代码具有同步代码的可读性
基本用法
async function fetchUserData() {
try {
const response = await fetch('/api/user');
const user = await response.json();
const postsResponse = await fetch(`/api/posts/${user.id}`);
const posts = await postsResponse.json();
return { user, posts };
} catch (error) {
console.error('数据获取失败:', error);
throw error; // 向上传播错误
} finally {
console.log('请求完成');
}
}
关键特性
- async 函数总是返回 Promise
- await 只能在 async 函数中使用
- 错误处理使用 try/catch 结构
- 可结合 Promise 方法 实现复杂控制流
四、现代异步模式
1. 顶级 await (ES2022) 在模块顶层直接使用 await:
// 模块中直接使用
const data = await fetchData();
console.log(data);
2. 异步迭代器 处理异步数据流:
async function processStream(stream) {
for await (const chunk of stream) {
console.log('接收到数据块:', chunk);
// 处理每个数据块
}
}
3. Promise 组合模式
// 超时控制
async function fetchWithTimeout(url, timeout = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeoutId);
return response.json();
} catch (error) {
clearTimeout(timeoutId);
throw new Error(`请求超时: ${timeout}ms`);
}
}
五、事件循环机制 理解异步的核心 - Event Loop 工作原理
graph LR
A[调用栈] --> B{栈空?}
B -->|是| C[任务队列]
C --> D[取第一个任务]
D --> E[执行任务]
E --> A
B -->|否| F[继续执行]
- 调用栈 (Call Stack):同步代码执行位置
- 任务队列 (Task Queue):宏任务队列(setTimeout、I/O)
- 微任务队列 (Microtask Queue):Promise、MutationObserver
执行顺序:
- 执行同步代码
- 执行所有微任务
- 执行一个宏任务
- 重复步骤2-3
六、异步编程最佳实践
- 避免回调地狱:使用 Promise 或 async/await 扁平化代码
- 错误处理:始终捕获可能的异常
- 并行优化:使用
Promise.all()处理独立异步操作 - 资源清理:使用
finally或try/finally确保资源释放 - 取消机制:使用 AbortController 实现可取消操作
- 节流控制:限制并发请求数量
async function throttleRequests(urls, maxConcurrent = 5) { const results = []; for (let i = 0; i < urls.length; i += maxConcurrent) { const batch = urls.slice(i, i + maxConcurrent); const batchResults = await Promise.all(batch.map(url => fetch(url))); results.push(...batchResults); } return results; }
七、常见异步场景解决方案
| 场景 | 解决方案 |
|---|---|
| 顺序执行 | async/await 顺序调用 |
| 并行执行 | Promise.all() |
| 首个成功 | Promise.any() |
| 超时控制 | AbortController + setTimeout |
| 重试机制 | 递归+指数退避 |
| 流式处理 | 异步迭代器 |
八、性能优化技巧
- 减少微任务数量:避免不必要的 Promise 封装
- 批量处理:合并多个异步操作
- 延迟执行:使用 requestIdleCallback 处理非关键任务
- Web Workers:将CPU密集型任务移出主线程
- 内存管理:及时取消不再需要的异步操作
浏览器支持与兼容性
| 特性 | 支持版本 |
|---|---|
| Promise | Chrome 32+, Firefox 29+, Safari 8+ |
| async/await | Chrome 55+, Firefox 52+, Safari 10.1+ |
| 顶级 await | Chrome 89+, Firefox 89+, Safari 15+ |
| Node.js | 全部特性从 v14+ 开始稳定支持 |
总结:异步编程选择指南
graph TD
A[简单异步] --> B(回调函数)
C[链式操作] --> D(Promise)
E[复杂逻辑] --> F(async/await)
G[流处理] --> H(异步迭代器)
I[并行任务] --> J(Promise.all)
B --> D
D --> F
F --> H
F --> J
掌握 JavaScript 异步编程是现代前端和后端开发的必备技能。从回调函数到 async/await 的演进,不仅改善了代码可读性和可维护性,还提供了更强大的异步控制能力。根据具体场景选择合适的异步模式,结合性能优化技巧,可以构建高效可靠的 JavaScript 应用。
🤔 参考 12
JavaScript 异步编程全面解析
一、JavaScript 异步编程的核心概念
1. 单线程与事件循环
- 单线程模型:JS 只有一个主线程执行代码
- 事件循环机制:协调主线程、消息队列和微任务队列
- 阻塞问题:同步操作会阻塞整个线程执行
2. 异步编程演进史
graph LR
A[回调函数] --> B[Promise]
B --> C[Generator]
C --> D[Async/Await]
二、异步解决方案详解
1. 回调函数(Callback)
function fetchData(callback) {
setTimeout(() => {
callback('数据加载完成');
}, 1000);
}
fetchData(result => console.log(result));
问题:
- 回调地狱(Callback Hell)
- 错误处理困难
- 流程控制复杂
2. Promise
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
Math.random() > 0.5
? resolve('成功')
: reject('失败');
}, 1000);
});
promise
.then(handleSuccess)
.catch(handleError)
.finally(cleanup);
Promise 状态图:
stateDiagram-v2
[*] --> Pending
Pending --> Fulfilled: resolve()
Pending --> Rejected: reject()
Fulfilled --> [*]
Rejected --> [*]
3. Async/Await
async function fetchUserData() {
try {
const response = await fetch('/api/user');
const data = await response.json();
return processData(data);
} catch (error) {
console.error('获取数据失败:', error);
return null;
} finally {
console.log('请求完成');
}
}
三、现代异步编程最佳实践
1. 错误处理策略
// 统一错误处理中间件
async function errorHandler(fn) {
try {
return await fn();
} catch (error) {
logError(error);
return fallbackValue;
}
}
// 使用
const data = await errorHandler(() => fetchData());
2. 并发控制模式
// 自定义并发控制器
function createConcurrencyPool(max) {
const queue = [];
let active = 0;
const next = () => {
if (active < max && queue.length) {
const { task, resolve, reject } = queue.shift();
active++;
task()
.then(resolve)
.catch(reject)
.finally(() => {
active--;
next();
});
}
};
return task => new Promise((resolve, reject) => {
queue.push({ task, resolve, reject });
next();
});
}
// 使用示例
const limitedFetch = createConcurrencyPool(3);
const results = await Promise.all(urls.map(url =>
limitedFetch(() => fetch(url))
);
3. 取消机制实现
function createCancellablePromise(executor) {
let rejectFn;
const cancelToken = new Promise((_, reject) => {
rejectFn = reject;
});
const promise = new Promise(executor);
return {
promise: Promise.race([promise, cancelToken]),
cancel: reason => rejectFn(new CancelledError(reason))
};
}
// 使用
const { promise, cancel } = createCancellablePromise(resolve => {
setTimeout(() => resolve('完成'), 5000);
});
// 用户取消
cancelButton.onclick = () => cancel('用户取消');
四、高级异步模式
1. 异步迭代器(Async Iterators)
async function* asyncGenerator() {
let i = 0;
while (i < 5) {
await sleep(1000);
yield i++;
}
}
(async () => {
for await (const num of asyncGenerator()) {
console.log(num);
}
})();
2. 响应式编程(ReactiveX)
import { fromEvent } from 'rxjs';
import { throttleTime, map } from 'rxjs/operators';
fromEvent(document, 'mousemove')
.pipe(
throttleTime(100),
map(event => ({ x: event.clientX, y: event.clientY }))
)
.subscribe(pos => {
console.log('鼠标位置:', pos);
});
3. Web Workers 多线程
// 主线程
const worker = new Worker('worker.js');
worker.postMessage({ cmd: 'start', data: 1000000 });
worker.onmessage = e => {
console.log('计算结果:', e.data);
};
// worker.js
self.onmessage = e => {
if (e.data.cmd === 'start') {
const result = heavyCalculation(e.data.data);
self.postMessage(result);
}
};
function heavyCalculation(n) {
// 复杂计算
return n * 2;
}
五、性能优化策略
1. 异步操作优化矩阵
| 操作类型 | 推荐方案 | 示例 |
|---|---|---|
| 顺序执行 | Async/Await | await step1(); await step2() |
| 并行独立 | Promise.all | Promise.all([task1(), task2()]) |
| 竞速操作 | Promise.race | Promise.race([fetch, timeout]) |
| 批量处理 | 分页并发 | batchProcess(items, 100) |
| 流处理 | 异步迭代器 | for await (const item of stream) |
2. 内存管理技巧
// 避免内存泄漏
function setupEventListeners() {
const data = getLargeData();
element.addEventListener('click', () => {
// 使用 data
});
// 清理函数
return () => {
element.removeEventListener('click');
data.releaseResources();
};
}
// 使用
const cleanup = setupEventListeners();
// 组件卸载时
cleanup();
六、错误监控与调试
1. 全局错误捕获
// 浏览器环境
window.addEventListener('error', handleGlobalError);
window.addEventListener('unhandledrejection', handlePromiseRejection);
// Node.js 环境
process.on('uncaughtException', handleGlobalError);
process.on('unhandledRejection', handlePromiseRejection);
2. 异步堆栈追踪
// 启用异步堆栈追踪
Error.stackTraceLimit = 50;
// 自定义异步操作标识
async function fetchData() {
const stack = new Error().stack;
try {
return await actualFetch();
} catch (err) {
err.asyncStack = stack;
throw err;
}
}
七、现代浏览器异步API
1. 网络请求
// Fetch API
const controller = new AbortController();
fetch('/api/data', {
signal: controller.signal,
cache: 'no-cache',
priority: 'high'
})
.then(processResponse)
.catch(handleError);
// 取消请求
controller.abort();
2. 后台任务
// 使用 requestIdleCallback
requestIdleCallback(deadline => {
while (deadline.timeRemaining() > 0) {
performBackgroundTask();
}
}, { timeout: 2000 });
3. 动画与渲染
// 使用 requestAnimationFrame
function animate() {
// 更新动画状态
updateAnimation();
// 继续下一帧
requestAnimationFrame(animate);
}
animate();
八、Node.js 异步特性
1. EventEmitter 模式
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', (data) => {
console.log('事件触发:', data);
});
myEmitter.emit('event', { value: 42 });
2. 流处理(Streams)
const fs = require('fs');
const readStream = fs.createReadStream('input.txt');
const writeStream = fs.createWriteStream('output.txt');
readStream
.pipe(transformStream) // 转换流
.pipe(writeStream)
.on('finish', () => console.log('处理完成'))
.on('error', handleError);
3. 集群模式(Cluster)
const cluster = require('cluster');
const os = require('os');
if (cluster.isPrimary) {
// 主进程
const cpus = os.cpus().length;
for (let i = 0; i < cpus; i++) {
cluster.fork();
}
cluster.on('exit', (worker) => {
console.log(`Worker ${worker.id} 退出`);
cluster.fork(); // 重启工作进程
});
} else {
// 工作进程
require('./server.js');
}
九、未来发展趋势
1. WebAssembly 异步交互
// 异步加载 WebAssembly
WebAssembly.instantiateStreaming(fetch('module.wasm'), imports)
.then(({ instance }) => {
const result = instance.exports.compute(1000);
console.log('WASM 计算结果:', result);
});
2. 三方库演进
- RxJS:响应式异步编程
- Bluebird:增强 Promise 功能
- Async.js:传统回调模式管理
- Redux-Saga:管理应用副作用
十、总结:异步编程工具箱
| 场景 | 推荐方案 | 示例 |
|---|---|---|
| 简单请求 | Fetch + Async/Await | await fetch(url) |
| 复杂流程 | Async/Await + try/catch | 多步带错误处理 |
| 并行操作 | Promise.all | 同时请求多个资源 |
| 竞速操作 | Promise.race | 请求超时处理 |
| 事件驱动 | EventEmitter | 自定义事件系统 |
| 数据流 | Streams/RxJS | 实时数据处理 |
| 计算密集型 | Web Workers | 后台计算任务 |
| 高并发 | 连接池/集群 | 数据库/服务器集群 |
graph TD
A[异步需求] --> B{类型}
B -->|简单请求| C[Fetch/Async]
B -->|复杂流程| D[Async/Await]
B -->|并行处理| E[Promise.all]
B -->|事件驱动| F[EventEmitter]
B -->|数据流| G[Streams/RxJS]
B -->|CPU密集型| H[Web Workers]
B -->|I/O密集型| I[Cluster/连接池]
掌握 JavaScript 异步编程需要理解:
- 事件循环机制
- Promise 核心原理
- Async/Await 工作方式
- 不同场景的最佳实践
- 性能优化与错误处理
随着 JavaScript 生态的不断发展,异步编程模式也在持续演进,但核心原则不变:高效利用单线程,避免阻塞,合理管理异步操作。
🤔 参考 13
JavaScript 异步编程深度指南
JavaScript 的异步编程模型是其核心特性之一,理解它对于构建高性能应用至关重要。下面我将全面解析 JavaScript 异步编程的各个方面。
异步编程基础
为什么需要异步? JavaScript 是单线程语言,异步操作可以:
- 避免阻塞主线程(UI 渲染和用户交互)
- 提高应用性能和响应速度
- 高效处理 I/O 密集型操作(网络请求、文件读写等)
事件循环机制 JavaScript 的并发模型基于"事件循环":
graph LR
A[调用栈] --> B{事件循环}
B --> C[任务队列]
C --> D[微任务队列]
D --> E[渲染更新]
E --> B
执行顺序:
- 执行同步代码(调用栈)
- 执行所有微任务(Promise、MutationObserver)
- 执行一个宏任务(setTimeout、事件回调)
- 重复步骤2-3
异步解决方案演进
1. 回调函数(Callback) 最早的异步解决方案:
function fetchData(callback) {
setTimeout(() => {
callback('Data received');
}, 1000);
}
fetchData(data => {
console.log(data); // 1秒后输出
});
问题:回调地狱(Callback Hell),难以维护
getUser(userId, user => {
getOrders(user.id, orders => {
getProducts(orders[0].id, product => {
// 更多嵌套...
});
});
});
2. Promise(ES6) 解决回调地狱,提供链式调用:
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
Math.random() > 0.5
? resolve('Success!')
: reject('Error!');
}, 1000);
});
}
fetchData()
.then(data => console.log(data))
.catch(err => console.error(err))
.finally(() => console.log('Cleanup'));
3. async/await(ES2017) 让异步代码看起来像同步代码:
async function processData() {
try {
const data = await fetchData();
const processed = await process(data);
console.log(processed);
} catch (error) {
console.error('Failed:', error);
} finally {
console.log('Operation completed');
}
}
关键异步 API 详解
Promise 方法
// 并行执行,全部成功
Promise.all([promise1, promise2])
.then(([result1, result2]) => { /*...*/ });
// 第一个完成(无论成功失败)
Promise.race([promise1, promise2])
.then(firstResult => { /*...*/ });
// 全部完成(无论成功失败)
Promise.allSettled([promise1, promise2])
.then(results => {
results.forEach(result =>
console.log(result.status) // fulfilled/rejected
);
});
// 第一个成功的结果
Promise.any([promise1, promise2])
.then(firstSuccess => { /*...*/ });
定时器函数
// 宏任务:当前任务队列执行完后执行
setTimeout(() => console.log('Timeout'), 0);
// 微任务:在当前任务结束后立即执行
queueMicrotask(() => console.log('Microtask'));
// 立即执行微任务
Promise.resolve().then(() => console.log('Promise'));
事件循环示例
console.log('Start');
setTimeout(() => console.log('Timeout'), 0);
Promise.resolve()
.then(() => console.log('Promise 1'))
.then(() => console.log('Promise 2'));
queueMicrotask(() => console.log('Microtask'));
console.log('End');
/* 输出顺序:
Start
End
Promise 1
Microtask
Promise 2
Timeout
*/
高级异步模式
1. 异步迭代器 处理异步数据流:
async function* asyncGenerator() {
let i = 0;
while (i < 3) {
await new Promise(resolve => setTimeout(resolve, 100));
yield i++;
}
}
(async () => {
for await (const num of asyncGenerator()) {
console.log(num); // 0, 1, 2 (每100ms输出)
}
})();
2. 取消异步操作 使用 AbortController:
const controller = new AbortController();
async function fetchWithCancel(url) {
try {
const response = await fetch(url, {
signal: controller.signal
});
return response.json();
} catch (err) {
if (err.name === 'AbortError') {
console.log('Fetch aborted');
}
}
}
// 取消请求
controller.abort();
3. 错误处理策略 全局错误捕获:
// 未捕获的 Promise 拒绝
window.addEventListener('unhandledrejection', event => {
console.error('Unhandled rejection:', event.reason);
});
// async 函数错误
async function riskyOperation() {
throw new Error('Something failed');
}
riskyOperation().catch(err => {
console.error('Caught:', err);
});
性能优化技巧
1. 并行 vs 顺序执行
// 顺序执行(慢)
async function sequential() {
const res1 = await fetch('/api1');
const res2 = await fetch('/api2');
return [res1, res2];
}
// 并行执行(快)
async function parallel() {
const [res1, res2] = await Promise.all([
fetch('/api1'),
fetch('/api2')
]);
return [res1, res2];
}
2. 限制并发数
async function concurrentMap(array, mapper, concurrency = 5) {
const results = [];
let index = 0;
async function worker() {
while (index < array.length) {
const i = index++;
results[i] = await mapper(array[i]);
}
}
await Promise.all(Array(concurrency).fill().map(worker));
return results;
}
// 使用
concurrentMap(urls, fetch, 3);
3. 延迟加载与缓存
function createLazyLoader(fetcher) {
let promise;
return async () => {
if (!promise) {
promise = fetcher();
}
return promise;
};
}
const getUser = createLazyLoader(() => fetch('/user'));
// 只会发起一次请求
getUser().then(/* ... */);
getUser().then(/* ... */);
实战应用场景
1. 表单提交
async function handleSubmit(e) {
e.preventDefault();
const submitBtn = e.target.querySelector('button[type="submit"]');
submitBtn.disabled = true;
try {
const formData = new FormData(e.target);
const response = await fetch('/submit', {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error('Server error');
}
showSuccess('Form submitted!');
} catch (error) {
showError(error.message);
} finally {
submitBtn.disabled = false;
}
}
2. 实时数据更新
async function* streamUpdates(endpoint) {
const response = await fetch(endpoint);
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
const events = chunk.split('\n\n');
for (const event of events) {
if (event) yield JSON.parse(event);
}
}
}
// 使用
(async () => {
for await (const update of streamUpdates('/updates')) {
console.log('New update:', update);
}
})();
总结与最佳实践
- 优先使用 async/await:提高代码可读性和可维护性
- 始终处理错误:使用 try/catch 或 .catch()
- 避免阻塞事件循环:将CPU密集型任务放入Worker
- 合理使用并发:并行独立操作,顺序处理依赖操作
- 取消不需要的操作:使用AbortController避免资源浪费
- 监控未处理拒绝:添加全局unhandledrejection监听器
- 考虑微任务优化:queueMicrotask比setTimeout(0)更高效
掌握这些异步编程技术,你将能够构建高性能、响应迅速的JavaScript应用,有效处理各种I/O操作和复杂业务逻辑。