Bootstrap

如何使用Promise实现多次请求?

在JavaScript中,使用Promise实现多次请求的核心在于理解串行执行并行执行的不同场景及对应的实现方法。以下是详细分步说明:

一、Promise基础回顾

Promise是异步编程的解决方案,具有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)。通过.then().catch()方法可处理结果和错误,而链式调用和静态方法(如Promise.all)则为多次请求提供了灵活的实现方式。


二、串行请求:链式调用

适用场景:多个请求存在依赖关系,后一个请求需要前一个请求的结果。

实现步骤:
  1. 每个请求返回Promise:将单个请求封装成返回Promise的函数。
  2. 通过.then()串联:利用.then()返回新Promise的特性,形成链式结构。
示例代码:
function request1() {
    return new Promise((resolve) => {
        setTimeout(() => resolve("请求1结果"), 1000);
    });
}

function request2(data) {
    return new Promise((resolve) => {
        setTimeout(() => resolve(`${data} → 请求2结果`), 1000);
    });
}

request1()
    .then(result => request2(result))
    .then(finalResult => console.log(finalResult)) // 输出:请求1结果 → 请求2结果
    .catch(error => console.error("链式调用错误:", error));

关键点

  • 每个.then()中的回调函数返回新的Promise,形成链式调用。
  • 错误会沿着链式结构传递到最近的.catch()

三、并行请求:Promise.all

适用场景:多个请求无依赖关系,需同时执行并等待所有结果。

实现步骤:
  1. 封装多个Promise函数:每个请求独立封装。
  2. 使用Promise.all()聚合:传入Promise数组,并行执行。
示例代码:
const fetchUser = () => fetch('/api/user').then(res => res.json());
const fetchPosts = () => fetch('/api/posts').then(res => res.json());
const fetchComments = () => fetch('/api/comments').then(res => res.json());

Promise.all([fetchUser(), fetchPosts(), fetchComments()])
    .then(([user, posts, comments]) => {
        console.log("用户数据:", user);
        console.log("帖子数据:", posts);
        console.log("评论数据:", comments);
    })
    .catch(error => console.error("并行请求失败:", error));

关键点

  • 所有Promise成功时,返回结果数组,顺序与输入一致。
  • 任一Promise失败时,立即触发.catch(),其他未完成的请求仍会继续执行但结果被忽略。

四、其他并行处理方案

1. Promise.allSettled
  • 特点:无论单个Promise成功或失败,均等待所有完成。
  • 适用场景:需要获取所有请求的最终状态(如批量操作日志记录)。
Promise.allSettled([fetchUser(), fetchPosts()])
    .then(results => {
        results.forEach(result => {
            if (result.status === "fulfilled") console.log("成功:", result.value);
            else console.error("失败:", result.reason);
        });
    });
2:Promise.race
  • 特点:返回第一个完成的Promise(无论成功或失败)。
  • 适用场景:竞速请求或超时处理。
const timeout = new Promise((_, reject) => 
    setTimeout(() => reject("请求超时"), 5000)
);

Promise.race([fetchUser(), timeout])
    .then(user => console.log("用户数据:", user))
    .catch(error => console.error("错误:", error)); // 5秒内未完成则触发超时

五、错误处理策略

  1. 链式调用中的错误冒泡:错误会跳过后续.then(),直达最近的.catch()

  2. 并行请求的错误处理

  • Promise.all需在末尾统一处理错误。
  • 若需部分容错,可在单个Promise内部处理错误后返回默认值:
const safeFetch = (url) => 
    fetch(url).catch(error => ({ error: true, message: error.message }));

3:全局错误监听

window.addEventListener('unhandledrejection', event => {
    console.error("未处理的Promise错误:", event.reason);
});

六、实际应用技巧

1:结合async/await

async function loadData() {
    try {
        const [user, posts] = await Promise.all([fetchUser(), fetchPosts()]);
        // 处理数据
    } catch (error) {
        console.error("加载失败:", error);
    }
}

2:动态生成Promise数组

const urls = ['/api/1', '/api/2', '/api/3'];
const requests = urls.map(url => fetch(url));
Promise.all(requests).then(/* ... */);

3:控制并发数:避免一次性发起过多请求。

// 使用第三方库(如p-limit)或手动实现队列

七、总结

  • 串行请求:使用链式调用,适合依赖型任务。
  • 并行请求Promise.all用于全成功场景,Promise.allSettled用于部分容错。
  • 错误处理:通过.catch()try/catch(配合async/await)确保健壮性。
  • 进阶方案:根据需求选择Promise.racePromise.any等,或结合工具库优化并发控制。
;