You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在JavaScript Promise函数中遍历API多页响应的实现方案

如何递归获取所有分页API数据并一次性返回完整结果?

你的思路是对的,用递归遍历所有分页,但问题出在递归调用时没有等待子请求完成就结束了当前Promise,导致最初的调用无法拿到所有数据。我来帮你修复这个问题,同时提供两种更清晰的实现方式:

方案一:用Async/Await实现(推荐,更易读)

Async/await可以让异步逻辑看起来像同步代码,递归时能直观地等待下一页数据返回再合并:

const getContacts = async (token, startFrom = null) => {
  let currentUrl = url;
  if (startFrom) {
    currentUrl = `${url}?${startFrom}`;
  }

  try {
    // 发起当前页请求
    const response = await fetch(currentUrl, {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
        // 注意:这里要补上token认证头,比如API用Bearer认证的话:
        // Authorization: `Bearer ${token}`
      },
    });
    const data = await response.json();

    // 检查是否有下一页
    const nextLink = data.links.find(link => link.rel === 'next');
    if (nextLink) {
      // 递归获取下一页数据,等待结果返回
      const nextPageData = await getContacts(token, nextLink.uri);
      // 合并当前页和后续所有页的数据(如果data.data是对象用展开运算符,是数组就用concat)
      return {
        data: { ...data.data, ...nextPageData.data }
      };
    } else {
      // 没有下一页,直接返回当前页数据
      return { data: data.data };
    }
  } catch (err) {
    // 抛出错误让调用方的catch处理
    throw err;
  }
};

方案二:修复你原来的Promise链式调用版本

如果你更习惯用传统的Promise写法,核心是要等待递归的Promise完成后再resolve当前Promise,不能直接调用递归就结束:

const getContacts = (token, allData = {}, startFrom = null) => new Promise((resolve, reject) => {
  let currentUrl = url;
  if (startFrom) {
    currentUrl = `${url}?${startFrom}`;
  }

  fetch(currentUrl, {
    method: 'GET',
    headers: {
      'Content-Type': 'application/json',
      // 同样记得补上token认证头
      // Authorization: `Bearer ${token}`
    },
  })
  .then(response => response.json())
  .then((data) => {
    // 合并当前页数据到allData
    Object.assign(allData, data.data);

    const nextLink = data.links.find(link => link.rel === 'next');
    if (nextLink) {
      // 关键:递归调用后,要在它的then里再resolve当前Promise
      getContacts(token, allData, nextLink.uri)
        .then(() => resolve({ data: allData }))
        .catch(reject);
    } else {
      // 没有下一页,直接返回合并后的所有数据
      resolve({ data: allData });
    }
  })
  .catch(reject);
});

调用方式(和原来一致)

不管用哪种方案,调用代码都不用改,现在data.data会包含所有分页的完整数据:

getContacts(token)
  .then((data) => {
    const contacts = data.data;
    console.log(contacts); // 现在是所有页的集合了
  })
  .catch(err => console.error(err));

几个注意点

  1. 认证头补充:你原来的代码里fetch没有带token,这大概率会导致API请求失败,记得根据API要求添加认证头(比如Bearer Token)。
  2. 数据结构适配:如果data.data是数组而不是对象,合并时要改成allData.push(...data.data),返回时return { data: allData }。
  3. 递归安全:可以考虑添加递归深度限制,防止API返回无限循环的next链接导致栈溢出或无限请求。

内容的提问来源于stack exchange,提问作者 rok

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:30:38