JS中同时发起Ajax请求导致数据返回异常问题排查
解决异步Ajax调用的顺序问题,正确生成Twitch主播Div
看起来你踩中了异步操作最常见的坑——Ajax请求默认不会阻塞代码执行,导致依赖前序请求结果的逻辑提前跑起来,数组还没填充好就被使用,自然会抛出异常。别担心,我们有几种优雅的方式解决这个问题,核心思路就是确保所有需要的异步请求完成后,再执行创建Div的逻辑。
方案一:用async/await + Promise封装(最直观易读)
首先把原生Ajax请求封装成Promise,这样就能用async/await来控制流程顺序,让代码看起来像同步执行一样:
// 封装Ajax为可复用的Promise函数 function ajaxGet(url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.responseType = 'json'; xhr.onload = () => { if (xhr.status >= 200 && xhr.status < 300) { resolve(xhr.response); } else { reject(new Error(`请求失败:${xhr.statusText}`)); } }; xhr.onerror = () => reject(new Error('网络请求出错')); xhr.send(); }); } // 用async函数包裹主流程,严格控制执行顺序 async function loadAndRenderStreamers() { try { // 第一步:完成两次Ajax调用,把结果存入数组 // 用Promise.all并行执行两次请求,效率更高且保证顺序 const [firstStreamerId, secondStreamerId] = await Promise.all([ ajaxGet('你的第一个获取ID的接口URL'), ajaxGet('你的第二个获取ID的接口URL') ]); const streamerIds = [firstStreamerId, secondStreamerId]; // 第二步:确保数组已填充,再批量请求Twitch直播数据 // 同样用Promise.all并行请求,结果数组顺序和streamerIds完全对应 const streamerDataArray = await Promise.all( streamerIds.map(id => ajaxGet(`https://api.twitch.tv/helix/streams?user_login=${id}`)) ); // 第三步:创建Div并填充直播数据 streamerDataArray.forEach((data, index) => { const streamerId = streamerIds[index]; const streamerDiv = document.createElement('div'); streamerDiv.id = streamerId; // 根据Twitch返回的数据渲染内容 const streamInfo = data.data[0]; if (streamInfo) { streamDiv.innerHTML = ` <h3>主播:${streamerId}</h3> <p>直播标题:${streamInfo.title}</p> <p>当前观看人数:${streamInfo.viewer_count}</p> `; } else { streamDiv.innerHTML = `<h3>主播:${streamerId}(当前离线)</h3>`; } document.body.appendChild(streamerDiv); }); } catch (error) { console.error('处理过程出错:', error); } } // 启动整个流程 loadAndRenderStreamers();
为什么这个方案有效?
await会暂停async函数的执行,直到前面的Promise完成,确保我们拿到所有主播ID后才去请求Twitch数据。Promise.all让多个异步请求并行执行,既保证了结果顺序和输入顺序一致,又比串行请求效率更高。
方案二:用Promise链式调用(兼容旧环境)
如果你的环境不支持async/await(比如旧版本浏览器),可以用Promise的then链式调用来控制顺序:
// 同样先封装Ajax为Promise function ajaxGet(url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.responseType = 'json'; xhr.onload = () => { if (xhr.status >= 200 && xhr.status < 300) { resolve(xhr.response); } else { reject(new Error(`请求失败:${xhr.statusText}`)); } }; xhr.onerror = () => reject(new Error('网络请求出错')); xhr.send(); }); } // 通过链式调用控制流程顺序 Promise.all([ ajaxGet('你的第一个获取ID的接口URL'), ajaxGet('你的第二个获取ID的接口URL') ]) .then(([id1, id2]) => { const streamerIds = [id1, id2]; // 并行请求Twitch数据,同时传递主播ID数组 return Promise.all(streamerIds.map(id => ajaxGet(`https://api.twitch.tv/helix/streams?user_login=${id}`))) .then(dataArray => ({ streamerIds, dataArray })); }) .then(({ streamerIds, dataArray }) => { // 创建Div并填充数据 dataArray.forEach((data, index) => { const streamerId = streamerIds[index]; const div = document.createElement('div'); div.id = streamerId; // 填充内容逻辑同上 document.body.appendChild(div); }); }) .catch(error => console.error('出错:', error));
核心思路总结
异步问题的本质是代码执行顺序不按书写顺序来,所以我们需要用Promise(或现代的async/await)把异步操作变成可控制的“步骤”,确保前一步的结果完全就绪后,再执行下一步。这样就不会出现数组还没填充就被使用的异常了。
内容的提问来源于stack exchange,提问作者mso4491
相关产品推荐
相关产品推荐

