实际业务中,我们常会遇到两种典型场景:一是接口间存在依赖关系,需按顺序执行(串联调用);二是接口间相互独立,可并行执行以提升效率(并联调用)。
目前主流的异步处理方案有两种:Promise.then 链式调用与 async/await 同步化语法。本文将结合业务场景,讲解两种方案在串联、并联调用中的实现方式。
1、Promise
Promise 是 JavaScript 异步编程的核心规范,解决了传统回调地狱问题。其核心能力在于:通过 then 方法实现异步逻辑的链式串联。
1.1 Promise实现并联调用
无依赖关系的接口可同时发起请求,无需等待彼此完成,总耗时取决于最慢的接口(而非所有接口耗时之和)。但需注意:直接调用接口仅能实现 "并行发起",若需统一处理结果或错误,必须配合 Promise.all。
场景:页面初始化时,需同时加载三个独立数据:商品列表、用户信息、购物车数据,无任何依赖关系。
基础并联:仅并行发起请求(无统一处理)
若无需等待所有接口完成,仅需各自返回后执行独立逻辑,可直接调用接口函数:
js
// 模拟接口请求函数
function fetchGoods() {
return new Promise((resolve) => {
setTimeout(() => {
console.log("商品列表获取成功");
resolve([{ id: 1, name: "商品A" }, { id: 2, name: "商品B" }]);
}, 1500);
});
}
function fetchUserInfo() {
return new Promise((resolve) => {
setTimeout(() => {
console.log("用户信息获取成功");
resolve({ id: 100, name: "李四" });
}, 1000);
});
}
function fetchCart() {
return new Promise((resolve) => {
setTimeout(() => {
console.log("购物车数据获取成功");
resolve([{ goodsId: 1, count: 2 }, { goodsId: 2, count: 1 }]);
}, 2000);
});
}
// 直接并行发起请求,各自处理结果
fetchGoods().then(goods => renderGoodsList(goods)); // 渲染商品列表
fetchUserInfo().then(user => renderUserAvatar(user)); // 渲染用户头像
fetchCart().then(cart => renderCartCount(cart)); // 渲染购物车数量
进阶并联:用 Promise.all 统一处理结果与错误
若后续逻辑依赖所有接口的返回数据,或需统一处理错误(如任意接口失败则显示 "加载失败"),则需使用 Promise.all。
js
// 并联调用:使用 Promise.all() 同时执行
Promise.all([fetchGoods(), fetchUserInfo(), fetchCart()])
.then(([goods, user, cart]) => {
// 所有接口都成功后,统一处理结果(总耗时取决于最慢的接口,这里是2秒)
console.log("所有请求完成,结果汇总:");
console.log("商品列表:", goods);
console.log("用户信息:", user);
console.log("购物车数据:", cart);
// 统一执行后续逻辑(如渲染完整页面)
renderDashboard(goods, user, cart);
})
.catch(error => {
// 只要有一个接口失败,就会立即触发(失败快速反馈)
console.error("至少一个请求失败:", error);
});
1.2 Promise实现串联调用
利用 then 方法的链式特性,前一个接口的返回结果作为后一个接口的入参,确保接口按顺序执行。其关键在于:then 回调中返回的 Promise 实例,会将状态与结果传递给下一个 then。
场景:需按顺序获取数据:先获取用户信息(拿到 userId)→ 用 userId 获取订单列表(拿到 orderId)→ 用 orderId 获取订单详情,接口间存在依赖。
js
// 模拟接口请求函数
function fetchUser() {
return new Promise((resolve) => {
setTimeout(() => {
console.log("获取用户信息成功");
resolve({ userId: 1001, name: "张三" }); // 返回用户信息
}, 1000);
});
}
function fetchOrders(userId) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(`根据用户ID=${userId}获取订单列表成功`);
resolve([{ orderId: 2001, goods: "手机" }, { orderId: 2002, goods: "电脑" }]); // 返回订单列表
}, 1000);
});
}
function fetchOrderDetail(orderId) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(`根据订单ID=${orderId}获取订单详情成功`);
resolve({ orderId, price: 5999, status: "已付款" }); // 返回订单详情
}, 1000);
});
}
// 串联调用:使用 .then() 链式执行
fetchUser()
.then(user => {
// 第一个接口完成后,调用第二个接口(传入用户ID)
return fetchOrders(user.userId);
})
.then(orders => {
// 第二个接口完成后,调用第三个接口(传入第一个订单ID)
return fetchOrderDetail(orders[0].orderId);
})
.then(detail => {
// 第三个接口完成后,处理最终结果
console.log("最终订单详情:", detail);
})
.catch(error => {
// 捕获整个链条中的错误
console.error("请求失败:", error);
});
2、async/await
async/await 是基于 Promise 实现的语法糖,核心优势是将异步代码 "同步化"。await关键字的意思是"等待",表示其后面的表达式需要等待,返回完成后再向下继续,代码结构更清晰,避免了 then 链式调用的嵌套感。
2.1 async/await实现串联调用
在 async 函数中,通过 await 关键字逐个等待接口完成,前一个接口的结果可直接作为后一个接口的参数,逻辑与同步代码完全一致。
场景:同 Promise 串联场景,依次获取用户信息 → 订单列表 → 订单详情
js
// 复用模拟接口函数(与 Promise 示例一致)
function fetchUser() {
return new Promise((resolve) => {
setTimeout(() => {
console.log("获取用户信息成功");
resolve({ userId: 1001, name: "张三" });
}, 1000);
});
}
function fetchOrders(userId) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(`根据用户ID=${userId}获取订单列表成功`);
resolve([{ orderId: 2001, goods: "手机" }, { orderId: 2002, goods: "电脑" }]);
}, 1000);
});
}
function fetchOrderDetail(orderId) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(`根据订单ID=${orderId}获取订单详情成功`);
resolve({ orderId, price: 5999, status: "已付款" });
}, 1000);
});
}
// async/await 串联调用
async function fetchDataInSeries() {
try {
// 第一步:获取用户信息(等待完成后再执行下一步)
const user = await fetchUser();
// 第二步:用用户ID获取订单列表(依赖上一步结果)
const orders = await fetchOrders(user.userId);
// 第三步:用订单ID获取详情(依赖上一步结果)
const detail = await fetchOrderDetail(orders[0].orderId);
// 所有步骤完成后处理结果
console.log("最终订单详情:", detail);
} catch (error) {
// 统一捕获整个串联过程中的错误(任何一步失败都会进入这里)
console.error("请求失败:", error);
}
}
// 执行函数
fetchDataInSeries();
2.2 async/await实现并联调用
async/await 本身不支持直接并行。这里仅给出使用async/await实现Promise.all进行统一处理的范例。
场景:同Promise并联场景,同时获取商品列表、用户信息、购物车数据。
js
// async/await 并联调用(结合 Promise.all)
async function fetchDataInParallel() {
try {
// 等待所有请求完成(总耗时取决于最慢的接口,这里是2秒)
const [goods, user, cart] = await Promise.all([fetchGoods(), fetchUserInfo(), fetchCart()]);
// 统一处理所有结果
console.log("所有请求完成,结果汇总:");
console.log("商品列表:", goods);
console.log("用户信息:", user);
console.log("购物车数据:", cart);
} catch (error) {
// 任意一个请求失败,都会进入这里
console.error("至少一个请求失败:", error);
}
}
// 执行函数
fetchDataInParallel();
总结
Promise和async/await都可以实现接口的串联和并联调用。并联调用直接进行多个接口的并发调用即可,如果需要对接口返回的数据进行统一处理,则需要配合Promise.all。而对于串联调用,async/await的同步化语法,使代码的线性排列与同步逻辑一致,可读性更高,是更好的调用方式。