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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:37:14