JS 异步编程与最佳实践
2025/9/17大约 11 分钟
JavaScript异步编程与最佳实践
学习目标
- 理解异步编程的基本概念和事件循环
- 掌握Promise的使用和链式调用
- 学会使用async/await简化异步代码
- 了解错误处理和最佳实践
异步编程基础
同步与异步
// 同步代码 - 阻塞执行
console.log('开始');
console.log('中间');
console.log('结束');
// 输出:开始 -> 中间 -> 结束
// 异步代码 - 非阻塞执行
console.log('开始');
setTimeout(() => {
console.log('异步操作');
}, 1000);
console.log('结束');
// 输出:开始 -> 结束 -> 异步操作(1秒后)
// 回调函数示例
function fetchData(callback) {
setTimeout(() => {
const data = { id: 1, name: '用户数据' };
callback(data);
}, 1000);
}
fetchData((data) => {
console.log('获取到数据:', data);
});
回调地狱问题
// 回调地狱示例
function getUserData(userId, callback) {
setTimeout(() => {
callback({ id: userId, name: '张三' });
}, 1000);
}
function getUserPosts(userId, callback) {
setTimeout(() => {
callback([{ id: 1, title: '文章1' }, { id: 2, title: '文章2' }]);
}, 1000);
}
function getPostComments(postId, callback) {
setTimeout(() => {
callback([{ id: 1, content: '评论1' }, { id: 2, content: '评论2' }]);
}, 1000);
}
// 嵌套回调 - 难以维护
getUserData(1, (user) => {
console.log('用户:', user);
getUserPosts(user.id, (posts) => {
console.log('文章:', posts);
getPostComments(posts[0].id, (comments) => {
console.log('评论:', comments);
// 更多嵌套...
});
});
});
事件循环机制
// 事件循环示例
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
// 输出顺序:1 -> 4 -> 3 -> 2
// 解释:
// 1. 同步代码先执行:1, 4
// 2. 微任务队列(Promise):3
// 3. 宏任务队列(setTimeout):2
// 更复杂的例子
setTimeout(() => console.log('timeout1'), 0);
setTimeout(() => console.log('timeout2'), 0);
Promise.resolve().then(() => {
console.log('promise1');
return Promise.resolve();
}).then(() => {
console.log('promise2');
});
console.log('sync');
// 输出:sync -> promise1 -> promise2 -> timeout1 -> timeout2
Promise详解
Promise基础
点击展开示例
// Promise的三种状态
// pending(等待中)-> fulfilled(已完成)或 rejected(已拒绝)
// 创建Promise
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve('操作成功');
} else {
reject(new Error('操作失败'));
}
}, 1000);
});
// 使用Promise
promise1
.then(result => {
console.log('成功:', result);
})
.catch(error => {
console.log('失败:', error.message);
})
.finally(() => {
console.log('操作完成');
});
// Promise链式调用
function fetchUser(id) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ id, name: `用户${id}` });
}, 1000);
});
}
function fetchUserPosts(userId) {
return new Promise((resolve) => {
setTimeout(() => {
resolve([{ id: 1, title: '文章1' }, { id: 2, title: '文章2' }]);
}, 1000);
});
}
// 链式调用解决回调地狱
fetchUser(1)
.then(user => {
console.log('用户:', user);
return fetchUserPosts(user.id);
})
.then(posts => {
console.log('文章:', posts);
return posts.length;
})
.then(count => {
console.log('文章数量:', count);
})
.catch(error => {
console.error('错误:', error);
});
Promise静态方法
点击展开示例
// Promise.all - 所有Promise都成功才成功
const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);
Promise.all([promise1, promise2, promise3])
.then(results => {
console.log('所有结果:', results); // [1, 2, 3]
})
.catch(error => {
console.log('有Promise失败:', error);
});
// Promise.allSettled - 等待所有Promise完成(无论成功失败)
const promises = [
Promise.resolve('成功1'),
Promise.reject('失败1'),
Promise.resolve('成功2')
];
Promise.allSettled(promises)
.then(results => {
console.log('所有结果:', results);
// [
// { status: 'fulfilled', value: '成功1' },
// { status: 'rejected', reason: '失败1' },
// { status: 'fulfilled', value: '成功2' }
// ]
});
// Promise.race - 第一个完成的Promise决定结果
const fastPromise = new Promise(resolve => setTimeout(() => resolve('快'), 100));
const slowPromise = new Promise(resolve => setTimeout(() => resolve('慢'), 1000));
Promise.race([fastPromise, slowPromise])
.then(result => {
console.log('最快结果:', result); // '快'
});
// Promise.any - 第一个成功的Promise决定结果
const failPromise1 = Promise.reject('失败1');
const failPromise2 = Promise.reject('失败2');
const successPromise = Promise.resolve('成功');
Promise.any([failPromise1, failPromise2, successPromise])
.then(result => {
console.log('第一个成功:', result); // '成功'
})
.catch(error => {
console.log('所有都失败:', error);
});
Promise实际应用
点击展开示例
// 封装fetch请求
function request(url, options = {}) {
return new Promise((resolve, reject) => {
fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
...options
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.json();
})
.then(data => resolve(data))
.catch(error => reject(error));
});
}
// 使用封装的请求
request('/api/users')
.then(users => {
console.log('用户列表:', users);
return request(`/api/users/${users[0].id}/posts`);
})
.then(posts => {
console.log('用户文章:', posts);
})
.catch(error => {
console.error('请求失败:', error.message);
});
// 并行请求
function fetchUserData(userId) {
const userPromise = request(`/api/users/${userId}`);
const postsPromise = request(`/api/users/${userId}/posts`);
const commentsPromise = request(`/api/users/${userId}/comments`);
return Promise.all([userPromise, postsPromise, commentsPromise])
.then(([user, posts, comments]) => {
return {
user,
posts,
comments,
summary: {
postsCount: posts.length,
commentsCount: comments.length
}
};
});
}
fetchUserData(1)
.then(userData => {
console.log('用户完整数据:', userData);
})
.catch(error => {
console.error('获取用户数据失败:', error);
});
async/await
基础用法
点击展开示例
// async函数总是返回Promise
async function fetchData() {
return '数据';
}
fetchData().then(data => console.log(data)); // '数据'
// await只能在async函数中使用
async function getUserInfo(userId) {
try {
const user = await fetchUser(userId);
console.log('用户:', user);
const posts = await fetchUserPosts(user.id);
console.log('文章:', posts);
return { user, posts };
} catch (error) {
console.error('获取用户信息失败:', error);
throw error;
}
}
// 使用async/await
getUserInfo(1)
.then(result => {
console.log('完整结果:', result);
})
.catch(error => {
console.error('最终错误:', error);
});
// 对比Promise链式调用
function getUserInfoWithPromise(userId) {
return fetchUser(userId)
.then(user => {
console.log('用户:', user);
return fetchUserPosts(user.id)
.then(posts => {
console.log('文章:', posts);
return { user, posts };
});
})
.catch(error => {
console.error('获取用户信息失败:', error);
throw error;
});
}
并行与串行
点击展开示例
// 串行执行(一个接一个)
async function serialExecution() {
console.time('串行执行');
const result1 = await fetchData(1); // 等待1秒
const result2 = await fetchData(2); // 再等待1秒
const result3 = await fetchData(3); // 再等待1秒
console.timeEnd('串行执行'); // 约3秒
return [result1, result2, result3];
}
// 并行执行(同时开始)
async function parallelExecution() {
console.time('并行执行');
const promise1 = fetchData(1); // 立即开始
const promise2 = fetchData(2); // 立即开始
const promise3 = fetchData(3); // 立即开始
const results = await Promise.all([promise1, promise2, promise3]);
console.timeEnd('并行执行'); // 约1秒
return results;
}
// 混合模式
async function mixedExecution() {
// 先并行获取用户和配置
const [user, config] = await Promise.all([
fetchUser(1),
fetchConfig()
]);
// 然后串行处理用户数据
const posts = await fetchUserPosts(user.id);
const processedPosts = await processPostsWithConfig(posts, config);
return { user, posts: processedPosts };
}
function fetchData(id) {
return new Promise(resolve => {
setTimeout(() => resolve(`数据${id}`), 1000);
});
}
错误处理
点击展开示例
// try-catch错误处理
async function handleErrors() {
try {
const data = await fetchData();
const processed = await processData(data);
const saved = await saveData(processed);
return saved;
} catch (error) {
if (error.name === 'NetworkError') {
console.log('网络错误,稍后重试');
// 重试逻辑
} else if (error.name === 'ValidationError') {
console.log('数据验证失败:', error.message);
// 数据修正逻辑
} else {
console.log('未知错误:', error);
// 通用错误处理
}
throw error; // 重新抛出错误
}
}
// 多个try-catch
async function multipleErrorHandling() {
let user, posts;
try {
user = await fetchUser(1);
} catch (error) {
console.error('获取用户失败:', error);
user = { id: 1, name: '默认用户' }; // 使用默认值
}
try {
posts = await fetchUserPosts(user.id);
} catch (error) {
console.error('获取文章失败:', error);
posts = []; // 使用空数组
}
return { user, posts };
}
// 自定义错误类
class APIError extends Error {
constructor(message, status, code) {
super(message);
this.name = 'APIError';
this.status = status;
this.code = code;
}
}
async function apiRequest(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new APIError(
`请求失败: ${response.statusText}`,
response.status,
'API_REQUEST_FAILED'
);
}
return await response.json();
} catch (error) {
if (error instanceof APIError) {
console.error(`API错误 [${error.code}]:`, error.message);
} else {
console.error('网络错误:', error.message);
}
throw error;
}
}
高级异步模式
点击展开示例
// 异步迭代器
async function* asyncGenerator() {
for (let i = 1; i <= 3; i++) {
await new Promise(resolve => setTimeout(resolve, 1000));
yield `数据${i}`;
}
}
async function useAsyncGenerator() {
for await (const data of asyncGenerator()) {
console.log('接收到:', data);
}
}
// 异步队列
class AsyncQueue {
constructor(concurrency = 1) {
this.concurrency = concurrency;
this.running = 0;
this.queue = [];
}
async add(task) {
return new Promise((resolve, reject) => {
this.queue.push({
task,
resolve,
reject
});
this.process();
});
}
async process() {
if (this.running >= this.concurrency || this.queue.length === 0) {
return;
}
this.running++;
const { task, resolve, reject } = this.queue.shift();
try {
const result = await task();
resolve(result);
} catch (error) {
reject(error);
} finally {
this.running--;
this.process();
}
}
}
// 使用异步队列
const queue = new AsyncQueue(2); // 最多同时执行2个任务
const tasks = Array.from({ length: 5 }, (_, i) =>
() => new Promise(resolve =>
setTimeout(() => resolve(`任务${i + 1}完成`), 1000)
)
);
async function runTasks() {
const promises = tasks.map(task => queue.add(task));
const results = await Promise.all(promises);
console.log('所有任务完成:', results);
}
// 重试机制
async function retry(fn, maxAttempts = 3, delay = 1000) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (error) {
if (attempt === maxAttempts) {
throw new Error(`重试${maxAttempts}次后仍然失败: ${error.message}`);
}
console.log(`第${attempt}次尝试失败,${delay}ms后重试...`);
await new Promise(resolve => setTimeout(resolve, delay));
delay *= 2; // 指数退避
}
}
}
// 使用重试
async function unreliableOperation() {
if (Math.random() < 0.7) {
throw new Error('操作失败');
}
return '操作成功';
}
retry(unreliableOperation, 3, 500)
.then(result => console.log(result))
.catch(error => console.error(error.message));
错误处理
错误处理策略
点击展开示例
// 全局错误处理
window.addEventListener('unhandledrejection', event => {
console.error('未处理的Promise拒绝:', event.reason);
event.preventDefault(); // 阻止默认的错误提示
});
// 错误边界类
class ErrorBoundary {
constructor() {
this.errors = [];
this.listeners = [];
}
catch(error, context = 'unknown') {
const errorInfo = {
error,
context,
timestamp: new Date(),
stack: error.stack
};
this.errors.push(errorInfo);
this.notifyListeners(errorInfo);
// 根据错误类型决定处理方式
if (error.name === 'NetworkError') {
this.handleNetworkError(error);
} else if (error.name === 'ValidationError') {
this.handleValidationError(error);
} else {
this.handleGenericError(error);
}
}
handleNetworkError(error) {
console.log('网络错误处理:', error.message);
// 显示网络错误提示
// 可能触发重试
}
handleValidationError(error) {
console.log('验证错误处理:', error.message);
// 显示表单验证错误
}
handleGenericError(error) {
console.error('通用错误处理:', error);
// 记录错误日志
// 显示通用错误提示
}
onError(listener) {
this.listeners.push(listener);
}
notifyListeners(errorInfo) {
this.listeners.forEach(listener => {
try {
listener(errorInfo);
} catch (e) {
console.error('错误监听器执行失败:', e);
}
});
}
getErrors() {
return [...this.errors];
}
clearErrors() {
this.errors = [];
}
}
// 使用错误边界
const errorBoundary = new ErrorBoundary();
errorBoundary.onError((errorInfo) => {
console.log('错误发生:', errorInfo.context, errorInfo.error.message);
});
// 包装异步函数
function withErrorBoundary(fn, context) {
return async (...args) => {
try {
return await fn(...args);
} catch (error) {
errorBoundary.catch(error, context);
throw error;
}
};
}
// 使用包装函数
const safeApiCall = withErrorBoundary(apiRequest, 'API调用');
safeApiCall('/api/users')
.then(users => console.log('用户:', users))
.catch(error => console.log('API调用失败'));
超时处理
点击展开示例
// 超时Promise
function timeout(ms) {
return new Promise((_, reject) => {
setTimeout(() => reject(new Error('操作超时')), ms);
});
}
// 为Promise添加超时
function withTimeout(promise, ms) {
return Promise.race([
promise,
timeout(ms)
]);
}
// 使用超时
async function fetchWithTimeout(url, timeoutMs = 5000) {
try {
const response = await withTimeout(fetch(url), timeoutMs);
return await response.json();
} catch (error) {
if (error.message === '操作超时') {
console.error(`请求超时 (${timeoutMs}ms):`, url);
}
throw error;
}
}
// 可取消的Promise
class CancellablePromise {
constructor(executor) {
this.cancelled = false;
this.promise = new Promise((resolve, reject) => {
this.reject = reject;
executor(
(value) => {
if (!this.cancelled) resolve(value);
},
(reason) => {
if (!this.cancelled) reject(reason);
}
);
});
}
cancel() {
this.cancelled = true;
this.reject(new Error('操作已取消'));
}
then(onFulfilled, onRejected) {
return this.promise.then(onFulfilled, onRejected);
}
catch(onRejected) {
return this.promise.catch(onRejected);
}
}
// 使用可取消Promise
const cancellableRequest = new CancellablePromise((resolve, reject) => {
const timeoutId = setTimeout(() => {
resolve('请求完成');
}, 3000);
// 清理函数
return () => clearTimeout(timeoutId);
});
// 2秒后取消请求
setTimeout(() => {
cancellableRequest.cancel();
}, 2000);
cancellableRequest
.then(result => console.log(result))
.catch(error => console.log(error.message)); // "操作已取消"
最佳实践
代码组织
点击展开示例
// 1. 优先使用async/await而不是Promise链
// ❌ 不推荐
function fetchUserData(id) {
return fetchUser(id)
.then(user => {
return fetchUserPosts(user.id)
.then(posts => {
return { user, posts };
});
});
}
// ✅ 推荐
async function fetchUserData(id) {
const user = await fetchUser(id);
const posts = await fetchUserPosts(user.id);
return { user, posts };
}
// 2. 合理使用并行和串行
// ❌ 不必要的串行
async function inefficientFetch() {
const user = await fetchUser(1);
const config = await fetchConfig(); // 可以并行
const posts = await fetchUserPosts(user.id);
return { user, config, posts };
}
// ✅ 合理的并行
async function efficientFetch() {
const [user, config] = await Promise.all([
fetchUser(1),
fetchConfig()
]);
const posts = await fetchUserPosts(user.id);
return { user, config, posts };
}
// 3. 统一的错误处理
class ApiService {
constructor(baseURL) {
this.baseURL = baseURL;
}
async request(endpoint, options = {}) {
try {
const response = await fetch(`${this.baseURL}${endpoint}`, {
headers: {
'Content-Type': 'application/json',
},
...options
});
if (!response.ok) {
throw new APIError(
`HTTP ${response.status}: ${response.statusText}`,
response.status
);
}
return await response.json();
} catch (error) {
this.handleError(error, endpoint);
throw error;
}
}
handleError(error, endpoint) {
console.error(`API请求失败 [${endpoint}]:`, error.message);
// 统一的错误日志记录
// 错误监控上报
}
async get(endpoint) {
return this.request(endpoint);
}
async post(endpoint, data) {
return this.request(endpoint, {
method: 'POST',
body: JSON.stringify(data)
});
}
}
// 4. 资源清理
class ResourceManager {
constructor() {
this.resources = new Set();
}
async createResource(factory) {
const resource = await factory();
this.resources.add(resource);
return resource;
}
async cleanup() {
const cleanupPromises = Array.from(this.resources).map(resource => {
if (resource.cleanup) {
return resource.cleanup();
}
});
await Promise.allSettled(cleanupPromises);
this.resources.clear();
}
}
// 使用示例
const api = new ApiService('https://api.example.com');
const resourceManager = new ResourceManager();
async function main() {
try {
const users = await api.get('/users');
console.log('用户列表:', users);
const newUser = await api.post('/users', {
name: '新用户',
email: 'new@example.com'
});
console.log('创建用户:', newUser);
} catch (error) {
console.error('主程序错误:', error);
} finally {
await resourceManager.cleanup();
}
}
性能优化
点击展开示例
// 1. 请求缓存
class CachedApiService extends ApiService {
constructor(baseURL, cacheTime = 5 * 60 * 1000) {
super(baseURL);
this.cache = new Map();
this.cacheTime = cacheTime;
}
async get(endpoint) {
const cacheKey = endpoint;
const cached = this.cache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < this.cacheTime) {
return cached.data;
}
const data = await super.get(endpoint);
this.cache.set(cacheKey, {
data,
timestamp: Date.now()
});
return data;
}
clearCache() {
this.cache.clear();
}
}
// 2. 防抖和节流
function debounce(fn, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn.apply(this, args), delay);
};
}
function throttle(fn, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
fn.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// 异步防抖
function asyncDebounce(fn, delay) {
let timeoutId;
let latestResolve;
return function(...args) {
return new Promise((resolve) => {
latestResolve = resolve;
clearTimeout(timeoutId);
timeoutId = setTimeout(async () => {
try {
const result = await fn.apply(this, args);
latestResolve(result);
} catch (error) {
latestResolve(Promise.reject(error));
}
}, delay);
});
};
}
// 使用异步防抖
const debouncedSearch = asyncDebounce(async (query) => {
const response = await fetch(`/api/search?q=${query}`);
return response.json();
}, 300);
// 3. 批量请求
class BatchRequestManager {
constructor(batchSize = 10, delay = 100) {
this.batchSize = batchSize;
this.delay = delay;
this.queue = [];
this.timeoutId = null;
}
async add(request) {
return new Promise((resolve, reject) => {
this.queue.push({ request, resolve, reject });
this.scheduleBatch();
});
}
scheduleBatch() {
if (this.timeoutId) return;
this.timeoutId = setTimeout(() => {
this.processBatch();
}, this.delay);
}
async processBatch() {
const batch = this.queue.splice(0, this.batchSize);
this.timeoutId = null;
if (batch.length === 0) return;
try {
const requests = batch.map(item => item.request());
const results = await Promise.allSettled(requests);
results.forEach((result, index) => {
const { resolve, reject } = batch[index];
if (result.status === 'fulfilled') {
resolve(result.value);
} else {
reject(result.reason);
}
});
} catch (error) {
batch.forEach(({ reject }) => reject(error));
}
// 处理剩余队列
if (this.queue.length > 0) {
this.scheduleBatch();
}
}
}
本章小结
通过本章学习,你应该掌握了:
- ✅ 异步编程的基本概念和事件循环机制
- ✅ Promise的创建、使用和静态方法
- ✅ async/await的语法和最佳实践
- ✅ 错误处理和异常管理策略
- ✅ 高级异步模式和性能优化技巧
- ✅ 现代JavaScript异步编程的最佳实践
异步编程是现代JavaScript开发的核心技能,掌握这些概念和技术将帮助你构建更高效、更可靠的应用程序。
学习完成
恭喜你完成了JavaScript基础到高级的学习之旅!现在你已经具备了扎实的JavaScript基础,可以开始探索更多的前端框架和高级主题了。