前端精读周刊:前端异步编程最佳实践
【免费下载链接】weekly 前端精读周刊。帮你理解最前沿、实用的技术。 项目地址: https://gitcode.com/GitHub_Trending/we/weekly
引言
你是否曾遇到过这样的情况:使用 async/await 编写的代码看似简洁,却在运行时出现了意想不到的性能问题?或者在处理多个异步操作时,不确定该使用 Promise.all 还是 Promise.race?本文将带你深入探讨前端异步编程的最佳实践,帮助你写出更高效、更可靠的异步代码。
读完本文后,你将能够:
- 理解 async/await 的优缺点及正确使用方式
- 掌握 Promise 串行和并行执行的实现方法
- 学会处理异步操作中的错误
- 了解 TypeScript 中异步类型的处理技巧
async/await:双刃剑的正确使用
async/await 语法糖极大地简化了异步代码的编写,但也容易被滥用,导致性能问题。在 前沿技术/55.精读《async await 是把双刃剑》.md 中提到,以下代码存在性能隐患:
(async () => {
const pizzaData = await getPizzaData(); // async call
const drinkData = await getDrinkData(); // async call
// ...
})();
问题在于,getPizzaData 和 getDrinkData 本可以并行执行,却被写成了串行执行,导致总执行时间增加。正确的做法是先创建 Promise 对象,再使用 await:
(async () => {
const pizzaPromise = getPizzaData();
const drinkPromise = getDrinkData();
const pizzaData = await pizzaPromise;
const drinkData = await drinkPromise;
// ...
})();
或者更简洁地使用 Promise.all:
(async () => {
const [pizzaData, drinkData] = await Promise.all([
getPizzaData(),
getDrinkData()
]);
// ...
})();
Promise 串行执行的实现
在处理需要按顺序执行的异步操作时,我们可以使用 Array.reduce 方法来实现。前沿技术/77.精读《用 Reduce 实现 Promise 串行执行》.md 中介绍了这种方法:
function runPromiseByQueue(myPromises) {
myPromises.reduce(
(previousPromise, nextPromise) => previousPromise.then(() => nextPromise()),
Promise.resolve()
);
}
使用示例:
const createPromise = (time, id) => () =>
new Promise(solve =>
setTimeout(() => {
console.log("promise", id);
solve();
}, time)
);
runPromiseByQueue([
createPromise(3000, 1),
createPromise(2000, 2),
createPromise(1000, 3)
]);
在 async/await 普及后,我们可以用更简洁的方式实现同样的功能:
async function runPromiseByQueue(myPromises) {
for (let value of myPromises) {
await value();
}
}
TypeScript 中的异步类型处理
在 TypeScript 中处理异步操作时,正确的类型定义非常重要。TS 类型体操/245.精读《Promise.all, Replace, Type Lookup...》.md 中介绍了如何实现一个类型安全的 PromiseAll 函数:
declare function PromiseAll<T>(values: T): Promise<{
[K in keyof T]: T[K] extends Promise<infer U> ? U : T[K]
}>
这个实现利用了 TypeScript 的映射类型和条件类型,能够正确推断出 Promise 数组解析后的类型。使用示例:
const promiseAllTest1 = PromiseAll([1, 2, 3] as const) // Promise<[1, 2, 3]>
const promiseAllTest2 = PromiseAll([1, 2, Promise.resolve(3)] as const) // Promise<[1, 2, 3]>
错误处理最佳实践
异步操作中,错误处理至关重要。以下是一些常见的错误处理模式:
- 使用 try/catch 处理单个异步操作:
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
} catch (error) {
console.error('Failed to fetch data:', error);
// 可以选择重新抛出错误,让调用者处理
throw error;
}
}
- 使用 Promise.catch 处理 Promise 链中的错误:
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => processData(data))
.catch(error => console.error('Error:', error));
- 处理 Promise.all 中的错误:
async function fetchMultipleResources() {
const promises = [
fetch('https://api.example.com/data1'),
fetch('https://api.example.com/data2'),
fetch('https://api.example.com/data3')
];
const results = await Promise.all(
promises.map(p => p.catch(error => ({ error })))
);
for (const result of results) {
if (result.error) {
console.error('Failed to fetch:', result.error);
} else {
// 处理成功的响应
const data = await result.json();
console.log('Data:', data);
}
}
}
异步编程的性能优化
-
合理使用并行执行:对于相互独立的异步操作,优先使用 Promise.all 进行并行执行。
-
避免过度并行:同时发起过多请求可能会导致网络拥塞或被服务器限制。可以使用分批处理的方式:
async function batchRequests(urls, batchSize = 5) {
const results = [];
for (let i = 0; i < urls.length; i += batchSize) {
const batch = urls.slice(i, i + batchSize);
const batchResults = await Promise.all(
batch.map(url => fetch(url).then(res => res.json()))
);
results.push(...batchResults);
}
return results;
}
- 使用缓存减少重复请求:
const requestCache = new Map();
async function fetchWithCache(url) {
if (requestCache.has(url)) {
return requestCache.get(url);
}
const response = await fetch(url);
const data = await response.json();
requestCache.set(url, data);
return data;
}
总结
异步编程是前端开发中不可或缺的一部分,掌握其最佳实践对于编写高效、可靠的代码至关重要。本文介绍了 async/await 的正确使用方法、Promise 串行和并行执行的实现、错误处理技巧以及性能优化策略。同时,我们还探讨了 TypeScript 中异步类型的处理方法。
希望通过本文的学习,你能够更加熟练地处理各种异步场景,编写出更高质量的前端代码。记住,技术本身没有绝对的好坏,关键在于如何根据具体场景合理运用。
推荐阅读
- 前沿技术/55.精读《async await 是把双刃剑》.md
- 前沿技术/77.精读《用 Reduce 实现 Promise 串行执行》.md
- TS 类型体操/245.精读《Promise.all, Replace, Type Lookup...》.md
- 前沿技术/30.精读《Javascript 事件循环与异步》.md
如果你对本文内容有任何疑问或建议,欢迎在评论区留言讨论。也欢迎点赞、收藏本文,关注我们获取更多前端技术干货!
【免费下载链接】weekly 前端精读周刊。帮你理解最前沿、实用的技术。 项目地址: https://gitcode.com/GitHub_Trending/we/weekly
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



