异步编程
概述
异步编程是JavaScript中处理非阻塞操作的核心技术,允许程序在等待耗时操作(如网络请求、文件读写)完成时继续执行其他任务。
JavaScript异步编程演进
1. 回调函数(Callback)
javascript
function fetchData(callback) {
setTimeout(() => {
callback('数据加载完成');
}, 1000);
}
fetchData((result) => {
console.log(result);
});回调地狱问题:
javascript
getUser(userId, function(user) {
getPosts(user.id, function(posts) {
getComments(posts[0].id, function(comments) {
// 嵌套层级过深
});
});
});2. Promise
javascript
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('数据加载完成');
}, 1000);
});
}
fetchData()
.then(result => console.log(result))
.catch(error => console.error(error));3. async/await
javascript
async function loadData() {
try {
const result = await fetchData();
console.log(result);
} catch (error) {
console.error(error);
}
}Promise详解
创建Promise
javascript
const promise = new Promise((resolve, reject) => {
// 异步操作
if (success) {
resolve(value);
} else {
reject(error);
}
});Promise方法
then()
javascript
promise.then(
value => console.log('成功:', value),
error => console.log('失败:', error)
);catch()
javascript
promise
.then(value => console.log(value))
.catch(error => console.error(error));finally()
javascript
promise
.then(value => console.log(value))
.catch(error => console.error(error))
.finally(() => console.log('操作完成'));Promise静态方法
Promise.all()
javascript
const promises = [
fetch('/api/user'),
fetch('/api/posts'),
fetch('/api/comments')
];
Promise.all(promises)
.then(results => {
console.log('所有请求完成:', results);
})
.catch(error => {
console.error('有一个请求失败:', error);
});Promise.race()
javascript
const timeout = new Promise((_, reject) => {
setTimeout(() => reject('请求超时'), 5000);
});
Promise.race([fetch('/api/data'), timeout])
.then(result => console.log('请求成功:', result))
.catch(error => console.error('错误:', error));Promise.allSettled()
javascript
const promises = [
Promise.resolve('成功1'),
Promise.reject('失败1'),
Promise.resolve('成功2')
];
Promise.allSettled(promises)
.then(results => {
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log('成功:', result.value);
} else {
console.log('失败:', result.reason);
}
});
});async/await最佳实践
错误处理
javascript
async function fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url);
return await response.json();
} catch (error) {
if (i === retries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * i));
}
}
}并行执行
javascript
async function loadAllData() {
const [user, posts, comments] = await Promise.all([
fetchUser(),
fetchPosts(),
fetchComments()
]);
return { user, posts, comments };
}顺序执行
javascript
async function processSequentially() {
const result1 = await step1();
const result2 = await step2(result1);
const result3 = await step3(result2);
return result3;
}实际应用场景
1. 网络请求
javascript
async function fetchUserData(userId) {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error('网络请求失败');
}
return await response.json();
}2. 文件操作(Node.js)
javascript
const fs = require('fs').promises;
async function readFileContent(filePath) {
try {
const content = await fs.readFile(filePath, 'utf-8');
console.log(content);
} catch (error) {
console.error('读取文件失败:', error);
}
}3. 数据库操作
javascript
async function getUserWithPosts(userId) {
const connection = await getDatabaseConnection();
try {
const user = await connection.query('SELECT * FROM users WHERE id = ?', [userId]);
const posts = await connection.query('SELECT * FROM posts WHERE user_id = ?', [userId]);
return { user, posts };
} finally {
connection.release();
}
}性能优化技巧
1. 避免不必要的await
javascript
// 不好
async function processItems(items) {
for (const item of items) {
await processItem(item); // 顺序执行,性能差
}
}
// 好
async function processItems(items) {
const promises = items.map(item => processItem(item));
await Promise.all(promises); // 并行执行
}2. 使用Promise缓存
javascript
const cache = new Map();
async function getCachedData(key, fetchFunction) {
if (cache.has(key)) {
return cache.get(key);
}
const promise = fetchFunction();
cache.set(key, promise);
try {
const result = await promise;
return result;
} catch (error) {
cache.delete(key);
throw error;
}
}3. 控制并发数量
javascript
async function limitedConcurrency(tasks, limit) {
const results = [];
const executing = [];
for (const task of tasks) {
const promise = task().then(result => {
executing.splice(executing.indexOf(promise), 1);
return result;
});
results.push(promise);
executing.push(promise);
if (executing.length >= limit) {
await Promise.race(executing);
}
}
return Promise.all(results);
}常见问题与解决方案
1. 内存泄漏
javascript
// 避免在闭包中保留不必要的引用
async function processLargeDataset() {
const data = await fetchLargeData();
// 及时清理不再需要的数据
processData(data);
// data = null; // 帮助垃圾回收
}2. 未处理的Promise拒绝
javascript
// 总是处理Promise拒绝
process.on('unhandledRejection', (reason, promise) => {
console.error('未处理的Promise拒绝:', reason);
});
// 或者使用catch
asyncFunction().catch(error => {
console.error('捕获错误:', error);
});3. 回调函数与Promise混合
javascript
// 将回调函数转换为Promise
function promisify(fn) {
return function(...args) {
return new Promise((resolve, reject) => {
fn(...args, (error, result) => {
if (error) reject(error);
else resolve(result);
});
});
};
}工具与库推荐
1. axios
- 基于Promise的HTTP客户端
- 请求/响应拦截器
- 自动转换JSON数据
2. bluebird
- 功能丰富的Promise库
- 性能优化
- 额外的实用方法
3. p-limit
- 控制Promise并发数量
- 轻量级解决方案
学习资源
书籍
- 《JavaScript异步编程》
- 《深入理解Promise》
在线资源
- MDN Web Docs: Promise
- JavaScript.info: Async/await
本文档涵盖了JavaScript异步编程的核心概念和最佳实践,后续会补充更多高级主题和实战案例。