如何等待包含异步函数调用的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
相关产品推荐
相关产品推荐

