JavaScript Promise 与 async/await 实战:5 个高频异步编程场景的优雅解决方案

JavaScript Promise 与 async/await 实战:5 个高频异步编程场景的优雅解决方案

在前端开发中,异步编程是绕不开的核心技能。从早期的回调函数到 Promise,再到如今的 async/await,JavaScript 的异步处理方式不断进化。本文将通过 5 个实际开发中的高频场景,带你掌握 Promise 与 async/await 的优雅用法。

场景一:串行执行多个异步任务

需求:按顺序执行多个接口请求,后一个请求依赖前一个请求的结果。

错误写法(回调地狱):

getUserData(userId, function(user) {

getOrders(user.id, function(orders) {

getOrderDetails(orders[0].id, function(details) {

console.log(details);

});

});

});

优雅写法(async/await):

async function fetchOrderDetails(userId) {

try {

const user = await getUserData(userId);

const orders = await getOrders(user.id);

const details = await getOrderDetails(orders[0].id);

return details;

} catch (error) {

console.error('获取订单详情失败:', error);

throw error;

}

}

关键点:await 让异步代码看起来像同步代码,逻辑清晰,错误处理统一。

场景二:并行执行多个独立异步任务

需求:同时发起多个互不依赖的请求,等待所有请求完成后统一处理。

错误写法(串行执行,效率低):

async function fetchAllData() {

const user = await getUserData();

const orders = await getOrders();

const products = await getProducts();

return { user, orders, products };

}

优雅写法(Promise.all 并行执行):

async function fetchAllData() {

try {

const [user, orders, products] = await Promise.all([

getUserData(),

getOrders(),

getProducts()

]);

return { user, orders, products };

} catch (error) {

console.error('批量获取数据失败:', error);

throw error;

}

}

性能对比:假设每个请求耗时 100ms,串行需要 300ms,并行只需 100ms。

进阶技巧:处理部分失败的情况

async function fetchAllDataWithFallback() {

const results = await Promise.allSettled([

getUserData(),

getOrders(),

getProducts()

]);

const user = results[0].status === 'fulfilled' ? results[0].value : null;

const orders = results[1].status === 'fulfilled' ? results[1].value : [];

const products = results[2].status === 'fulfilled' ? results[2].value : [];

return { user, orders, products };

}

场景三:异步请求超时控制

需求:防止接口响应过慢导致页面卡死,设置超时自动取消。

优雅写法(Promise.race 实现超时):

function fetchWithTimeout(url, timeout = 5000) {

const timeoutPromise = new Promise((_, reject) => {

setTimeout(() => reject(new Error('请求超时')), timeout);

});

return Promise.race([

fetch(url),

timeoutPromise

]);

}

async function fetchDataWithTimeout(url) {

try {

const response = await fetchWithTimeout(url, 5000);

const data = await response.json();

return data;

} catch (error) {

if (error.message === '请求超时') {

console.warn('请求超时,已自动取消');

// 可以显示友好的超时提示

}

throw error;

}

}

场景四:异步重试机制

需求:网络不稳定时自动重试,提高请求成功率。

优雅写法(指数退避重试):

async function fetchWithRetry(url, options = {}) {

const {

maxRetries = 3,

baseDelay = 1000,

timeout = 5000

} = options;

for (let attempt = 1; attempt <= maxRetries; attempt++) {

try {

const response = await fetchWithTimeout(url, timeout);

if (!response.ok) {

throw new Error(`HTTP ${response.status}`);

}

return await response.json();

} catch (error) {

if (attempt === maxRetries) {

console.error(`请求失败,已重试${maxRetries}次:`, error);

throw error;

}

// 指数退避:1s, 2s, 4s...

const delay = baseDelay * Math.pow(2, attempt - 1);

console.log(`第{attempt}次失败,{delay}ms 后重试...`);

await new Promise(resolve => setTimeout(resolve, delay));

}

}

}

使用示例:

const data = await fetchWithRetry('/api/user/profile', {

maxRetries: 3,

baseDelay: 1000,

timeout: 5000

});

场景五:请求取消(AbortController)

需求:用户快速切换页面时,取消未完成的请求,避免内存泄漏和数据错乱。

优雅写法(AbortController 取消请求):

function createCancelableFetch() {

const controller = new AbortController();

const fetchCancelable = async (url) => {

const response = await fetch(url, {

signal: controller.signal

});

return await response.json();

};

const cancel = () => {

controller.abort();

};

return { fetchCancelable, cancel };

}

实际应用(React 组件中):

useEffect(() => {

const { fetchCancelable, cancel } = createCancelableFetch();

async function loadData() {

try {

const data = await fetchCancelable('/api/data');

setData(data);

} catch (error) {

if (error.name !== 'AbortError') {

console.error('加载失败:', error);

}

}

}

loadData();

// 组件卸载或依赖变化时取消请求

return () => cancel();

}, [userId]);

综合实战:封装通用请求函数

将以上技巧整合,封装一个生产级别的请求函数:

async function request(url, options = {}) {

const {

method = 'GET',

data = null,

timeout = 5000,

maxRetries = 3,

needAuth = true

} = options;

// 添加认证头

const headers = {

'Content-Type': 'application/json',

...options.headers

};

if (needAuth) {

const token = localStorage.getItem('token');

if (token) {

headers['Authorization'] = `Bearer ${token}`;

}

}

// 重试逻辑

for (let attempt = 1; attempt <= maxRetries; attempt++) {

try {

const controller = new AbortController();

const timeoutId = setTimeout(() => controller.abort(), timeout);

const response = await fetch(url, {

method,

headers,

body: data ? JSON.stringify(data) : null,

signal: controller.signal

});

clearTimeout(timeoutId);

if (!response.ok) {

if (response.status === 401) {

// 未授权,跳转登录

window.location.href = '/login';

throw new Error('未授权');

}

throw new Error(`HTTP ${response.status}`);

}

return await response.json();

} catch (error) {

if (error.name === 'AbortError') {

console.warn('请求已取消');

throw error;

}

if (attempt === maxRetries) {

console.error('请求失败:', error);

throw error;

}

const delay = 1000 * Math.pow(2, attempt - 1);

await new Promise(resolve => setTimeout(resolve, delay));

}

}

}

使用示例:

const userData = await request('/api/user/profile');

const orders = await request('/api/orders', {

method: 'POST',

data: { productId: 123, quantity: 2 }

});

总结

  1. 串行依赖用 await,清晰直观

  2. 并行独立用 Promise.all,效率翻倍

  3. 超时控制用 Promise.race,防止卡死

  4. 网络不稳用指数退避重试,提高成功率

  5. 页面切换用 AbortController,避免泄漏

掌握这 5 个场景,你的异步代码将更加优雅、健壮、易维护。异步编程不再可怕,而是成为你的得力武器。

(完)

相关推荐
xcLeigh2 小时前
IoTDB Java 原生 API 实战:SessionPool 从入门到精通
java·开发语言·数据库·api·iotdb·sessionpool
杜子不疼.2 小时前
Java 智能体学习避坑指南:3 个常见误区,新手千万别踩,高效少走弯路
java·开发语言·人工智能·学习
冬天vs不冷2 小时前
为什么 Java 不让 Lambda 和匿名内部类修改外部变量?final 与等效 final 的真正意义
android·java·开发语言
星河耀银海2 小时前
JAVA 多线程编程:从基础原理到实战应用
java·开发语言·php
星河耀银海2 小时前
JAVA IO流:从基础原理到实战应用
java·服务器·开发语言
摸鱼仙人~2 小时前
Math.js 使用教程
开发语言·javascript·ecmascript
HAPPY酷3 小时前
Python高阶开发:从底层原理到架构设计的进阶之路
开发语言·python
wuhen_n3 小时前
LangChain Agents 实战:构建智能文件管理助手
前端·javascript·人工智能·langchain·ai编程
疯狂打码的少年3 小时前
【Day 6 Java转Python】字符串处理的“降维打击”
java·开发语言·python