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

如何在for循环中等待前一个fetch完成后再发起下一个请求?

解决异步Fetch循环中累加时间值不正确的问题

嘿,你的推测完全命中了问题的核心!问题就出在fetch是异步操作——你的for循环会同步地一口气发起所有请求,但每个请求的then回调要等请求完成后才会执行。这带来了两个关键问题:一是你用固定10秒的setTimeout来等所有请求完成太不可靠(万一某个请求10秒后还没返回,那累加就不完整);二是回调的执行顺序不一定和循环顺序一致,最终导致你的time变量只拿到了最后一个请求的结果(或者某个请求的结果覆盖了之前的)。

下面给你两种靠谱的解决方案,根据你的需求选就行:

方案一:按顺序执行请求(适合有依赖的场景)

如果你的请求之间有先后依赖(比如下一个请求需要用上一个的结果),或者你希望严格按数组顺序累加,那就用async/await把循环改成异步的,每次等当前请求完成后再进行下一次。这样不仅能保证累加顺序正确,还能彻底丢掉不靠谱的setTimeout。

修改后的代码:

// 把逻辑包在异步函数里
async function calculateTotalJourneyTime() {
  let totalTime = 0; // 用let声明,避免变量提升的坑
  // 这里替换成你实际的变量值
  const seconds = /* 你的秒数 */;
  const temp = /* 温度值 */;
  const dayNum = /* 星期几的数值 */;

  for (let i = 0; i < journeyArr.length; i++){ // 用let代替var,避免循环变量泄漏
    console.log("当前循环开始时的总时间: ", totalTime);
    const currentJourney = journeyArr[i];
    // 提取当前行程的参数
    const startStop = currentJourney.stops[0].id;
    const endStop = currentJourney.stops.at(-1).id; // 用at(-1)取最后一项更简洁
    const { routeid, lineid } = currentJourney; // 对象解构,代码更清爽

    // 等待当前fetch请求完成
    const response = await fetch(`http://localhost:8000/routes/api/predict/?lineid=${lineid}&start_stop=${startStop}&end_stop=${endStop}&routeid=${routeid}&time_secs=${seconds}&temp=${temp}&rain=0.16&dow=${dayNum}`);
    const data = await response.json();
    
    // 累加时间
    totalTime += data.journey_info.journey_time.hours * 60;
    totalTime += data.journey_info.journey_time.minutes;
  }

  // 所有请求完成后再计算并显示结果
  const hours = Math.floor(totalTime / 60);
  const minutes = totalTime % 60;
  timeLeft.innerHTML = `${hours} hour(s) ${minutes} minutes`;
}

// 调用函数启动计算
calculateTotalJourneyTime();

这个方案逻辑直观,顺序执行每个请求,缺点是总耗时等于所有请求耗时的总和,适合请求数量不多或者有依赖的场景。

方案二:并行发起所有请求(效率更高)

如果你的请求之间没有依赖关系,强烈推荐这个方案!所有请求同时发起,总耗时等于最慢的那个请求的时间,效率提升明显。我们用Promise.all来收集所有请求的结果,然后统一累加。

function calculateTotalJourneyTime() {
  // 这里替换成你实际的变量值
  const seconds = /* 你的秒数 */;
  const temp = /* 温度值 */;
  const dayNum = /* 星期几的数值 */;

  // 生成所有fetch请求的Promise数组,每个Promise返回当前行程的时间值
  const journeyTimePromises = journeyArr.map(journey => {
    const startStop = journey.stops[0].id;
    const endStop = journey.stops.at(-1).id;
    const { routeid, lineid } = journey;

    return fetch(`http://localhost:8000/routes/api/predict/?lineid=${lineid}&start_stop=${startStop}&end_stop=${endStop}&routeid=${routeid}&time_secs=${seconds}&temp=${temp}&rain=0.16&dow=${dayNum}`)
      .then(response => {
        // 处理HTTP错误,比如404、500
        if (!response.ok) throw new Error(`请求失败: ${response.status}`);
        return response.json();
      })
      .then(data => {
        // 计算当前行程的总分钟数并返回
        return data.journey_info.journey_time.hours * 60 + data.journey_info.journey_time.minutes;
      });
  });

  // 等待所有请求完成
  Promise.all(journeyTimePromises)
    .then(allJourneyTimes => {
      // 累加所有时间
      const totalTime = allJourneyTimes.reduce((sum, time) => sum + time, 0);
      // 计算并显示结果
      const hours = Math.floor(totalTime / 60);
      const minutes = totalTime % 60;
      timeLeft.innerHTML = `${hours} hour(s) ${minutes} minutes`;
    })
    .catch(error => {
      // 处理请求失败的情况,比如网络错误、HTTP错误
      console.error("计算总时间时出错:", error);
      timeLeft.innerHTML = "无法计算行程时间,请稍后重试";
    });
}

// 调用函数启动计算
calculateTotalJourneyTime();

这个方案效率更高,但要注意:如果任何一个请求失败,Promise.all会直接进入catch块。如果想允许部分请求失败,你可以改用Promise.allSettled,然后在结果中过滤掉失败的请求。

几个额外的小建议

  • 把var换成let或const,避免变量提升导致的意外问题,这是JavaScript开发的好习惯。
  • 永远不要用固定时长的setTimeout等待异步操作完成,这完全不可靠——网络状况是不可控的,用Promise的方法(await或Promise.all)才是正确的姿势。
  • 一定要添加错误处理,比如在fetch中检查response.ok,在Promise.all中添加catch,这样用户在请求失败时能得到友好提示,你也能快速定位问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 18:47:39