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

如何等待包含异步函数调用的forEach循环执行完毕

如何等待循环内所有XMLHttpRequest完成后执行后续操作

你的核心问题在于回调式的异步请求没办法便捷地等待全部完成——XMLHttpRequest本身是异步执行的,forEach会直接跑完整个循环,不会等待每个请求结束。解决思路是把你的requestAPI改造为Promise风格,然后用Promise.all()来监听所有请求的完成状态。

第一步:将requestAPI改造为Promise风格

把原来的回调逻辑替换成Promise的resolve和reject,这样我们就能利用Promise的异步流程控制能力:

function requestAPI(method, url, token = null, json = null) {
  return new Promise((resolve, reject) => {
    const rawFile = new XMLHttpRequest();
    rawFile.overrideMimeType('application/json');
    rawFile.open(method, url, true);
    
    if (token != null) {
      rawFile.setRequestHeader('Authorization', `Token ${token}`);
    }
    rawFile.setRequestHeader('Content-type', 'application/json; charset=utf-8;');
    
    rawFile.onreadystatechange = () => {
      if (rawFile.readyState === 4) {
        console.log(rawFile);
        // 根据HTTP状态码区分成功/失败
        if (rawFile.status >= 200 && rawFile.status < 300) {
          resolve(rawFile);
        } else {
          reject(new Error(`请求失败,状态码:${rawFile.status}`));
        }
      }
    };
    
    // 处理网络层面的错误
    rawFile.onerror = () => {
      reject(new Error('网络请求失败'));
    };
    
    rawFile.send(JSON.stringify(json));
  });
}

这里做了几个关键优化:

  • 让函数返回Promise对象,替代原来的回调参数
  • 请求成功(状态码2xx)时调用resolve,失败时调用reject
  • 新增onerror处理网络异常,避免遗漏错误场景

第二步:修改循环代码,用Promise.all等待所有请求完成

我们用map代替forEach(因为map可以直接收集每个请求返回的Promise),然后用Promise.all()等待所有请求完成后,再执行事件绑定操作:

// 收集所有请求的Promise实例
const requestPromises = campaigns.map((campaign, campaignIndex) => {
  return requestAPI('GET', `${API_ADDRESS}/call1/`, token, null)
    .then((response1) => {
      // 这里写向页面添加元素的代码
      // 可以返回添加后的元素,方便后续绑定事件
      const newElement = document.createElement('div');
      // 给元素设置内容/样式等
      newElement.textContent = campaign.name;
      document.body.appendChild(newElement);
      
      return { element: newElement, campaign };
    });
});

// 等待所有请求完成后执行后续操作
Promise.all(requestPromises)
  .then((results) => {
    // 所有元素都已添加到页面,开始绑定事件监听器
    results.forEach(({ element, campaign }) => {
      element.addEventListener('click', () => {
        console.log(`点击了活动:${campaign.name}`);
        // 你的事件处理逻辑
      });
    });
    console.log('所有请求完成,事件绑定完毕');
  })
  .catch((error) => {
    // 处理任意一个请求失败的情况
    console.error('有请求失败:', error);
  });

如果你的项目支持ES6+的async/await,还可以写成更简洁的异步函数形式:

async function processCampaigns() {
  try {
    const results = await Promise.all(campaigns.map((campaign, campaignIndex) => {
      return requestAPI('GET', `${API_ADDRESS}/call1/`, token, null)
        .then((response1) => {
          // 添加元素的代码
          const newElement = document.createElement('div');
          newElement.textContent = campaign.name;
          document.body.appendChild(newElement);
          return { element: newElement, campaign };
        });
    }));
    
    // 统一绑定事件
    results.forEach(({ element, campaign }) => {
      element.addEventListener('click', () => {
        // 事件逻辑
      });
    });
    console.log('所有操作完成');
  } catch (error) {
    console.error('处理失败:', error);
  }
}

// 调用执行
processCampaigns();

这样代码逻辑更直观,可读性也更高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:06:23