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

异步Fetch中setTimeout失效问题求助(JavaScript)

批量请求16k数据时的浏览器阻塞问题及解决方法

问题描述

需要获取约16k个对象的完整数据,仅获取ID时正常,但按ID逐个请求完整数据时Chrome无法处理。尝试添加Promise报错,添加常规1秒setTimeout后,程序会先等待1秒再一次性发起所有请求,而非每条间隔1秒。相关代码如下,且setTimeout仅能放在指定位置:

async function fetchMODEL(endpoint) {
  var array;
  const whatever = await fetch("https://link.com/" + endpoint)
    .then((response) => response.json())
    .then((data) => (array = data));
  return whatever;
}

function detectIDs(){
  fetchMODEL("cars")
    .then((response) => {
      var data = response;
      var allIds = data.map(item => 
      {return item.id;})
      //console.log(JSON.stringify(allIds)) 
      //return allIds; 
      
      var illus = [];
      for(i=0; i < allIds.length ;i++){
        //setTimeout(
        fetchMODEL("cars/" + allIds[i])
          .then((response) => {
          //console.log(typeof response.illustrator)
          var artistData = {};
            if(typeof response.illustrator == "undefined"){
              artistData.edit="EDIT";
              artistData.name = response.name;
              artistData.id = response.id;
              illus.push(artistData);
              console.log(artistData)
            }else{            
            artistData.id = response.id;
            artistData.illustrator = response.illustrator;
            artistData.name = response.name;
            illus.push(artistData);
            console.log(artistData);
            }
            })//,100)
       }
    })
}
var orgSetIDs = detectIDs();

问题原因

  1. 同步循环瞬间发起所有请求:原代码的for循环是同步执行的,会在极短时间内遍历完16k个ID并发起所有fetch请求。Chrome对同一域名的并发HTTP连接数有限制(通常为6个),大量请求排队会导致浏览器阻塞,甚至触发服务器的限流机制。
  2. setTimeout未实现间隔效果:直接在循环中添加固定延迟的setTimeout时,循环会瞬间将所有setTimeout任务加入事件队列,且所有任务的延迟时间相同,因此会在延迟结束后一次性触发所有请求,无法实现逐个间隔的效果。此外,原代码中用var声明的i是函数级作用域,所有setTimeout回调会共享同一个i值,最终都会使用循环结束后的i值(即16k),导致请求的ID错误。

解决方案

方案1:逐个请求+固定间隔(最稳妥)

改用async/await配合for...of循环,确保每个请求完成并等待指定间隔后再发起下一个请求,避免并发数过高:

// 简化fetchMODEL函数,统一用async/await风格
async function fetchMODEL(endpoint) {
  const response = await fetch(`https://link.com/${endpoint}`);
  if (!response.ok) {
    throw new Error(`请求失败: ${response.status}`);
  }
  return response.json();
}

async function detectIDs() {
  const data = await fetchMODEL("cars");
  const allIds = data.map(item => item.id);
  const illus = [];

  for (const id of allIds) {
    // 每个请求前等待1秒
    await new Promise(resolve => setTimeout(resolve, 1000));
    try {
      const response = await fetchMODEL(`cars/${id}`);
      const artistData = {
        id,
        name: response.name
      };
      if (typeof response.illustrator === "undefined") {
        artistData.edit = "EDIT";
      } else {
        artistData.illustrator = response.illustrator;
      }
      illus.push(artistData);
      console.log(artistData);
    } catch (err) {
      console.error(`请求ID ${id} 失败:`, err);
    }
  }
  return illus;
}

detectIDs();

方案2:批量并发请求(效率更高)

如果需要更快的速度,可以采用批量并发的方式,每次同时发起少量请求(比如10个),等待批次完成后再发起下一批,既控制并发数,又提高效率:

async function fetchMODEL(endpoint) {
  const response = await fetch(`https://link.com/${endpoint}`);
  if (!response.ok) {
    throw new Error(`请求失败: ${response.status}`);
  }
  return response.json();
}

async function detectIDs() {
  const data = await fetchMODEL("cars");
  const allIds = data.map(item => item.id);
  const illus = [];
  const batchSize = 10; // 每批并发10个请求

  for (let i = 0; i < allIds.length; i += batchSize) {
    const batchIds = allIds.slice(i, i + batchSize);
    // 发起当前批次的所有请求
    const batchPromises = batchIds.map(async id => {
      try {
        const response = await fetchMODEL(`cars/${id}`);
        const artistData = {
          id,
          name: response.name
        };
        if (typeof response.illustrator === "undefined") {
          artistData.edit = "EDIT";
        } else {
          artistData.illustrator = response.illustrator;
        }
        return artistData;
      } catch (err) {
        console.error(`请求ID ${id} 失败:`, err);
        return null;
      }
    });
    // 等待当前批次所有请求完成
    const batchResults = await Promise.all(batchPromises);
    // 过滤失败的请求结果并加入数组
    illus.push(...batchResults.filter(item => item !== null));
    // 批次之间间隔1秒(可选,根据服务器压力调整)
    await new Promise(resolve => setTimeout(resolve, 1000));
  }
  return illus;
}

detectIDs();

方案3:修复原setTimeout的间隔问题(不推荐)

如果必须使用原位置的setTimeout,可以通过let声明i(块级作用域)来确保每个setTimeout获取正确的ID,并设置递增的延迟时间:

async function fetchMODEL(endpoint) {
  var array;
  const whatever = await fetch("https://link.com/" + endpoint)
    .then((response) => response.json())
    .then((data) => (array = data));
  return whatever;
}

function detectIDs(){
  fetchMODEL("cars")
    .then((response) => {
      var data = response;
      var allIds = data.map(item => item.id);
      var illus = [];
      // 用let声明i,确保每个循环块的i独立
      for(let i=0; i < allIds.length ;i++){
        setTimeout(() => {
          fetchMODEL("cars/" + allIds[i])
            .then((response) => {
              var artistData = {};
              if(typeof response.illustrator == "undefined"){
                artistData.edit="EDIT";
                artistData.name = response.name;
                artistData.id = response.id;
                illus.push(artistData);
                console.log(artistData)
              }else{            
                artistData.id = response.id;
                artistData.illustrator = response.illustrator;
                artistData.name = response.name;
                illus.push(artistData);
                console.log(artistData);
              }
            })
        }, i*1000) // 每个请求延迟i*1000ms,实现间隔1秒
       }
    })
}
detectIDs();

注意:此方案会在瞬间创建16k个setTimeout任务,虽然能实现间隔请求,但浏览器事件队列会积压大量任务,仍可能影响性能,因此优先推荐方案1或方案2。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 18:05:34