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

如何使用XMLHttpRequest()正确循环处理HTTP请求并按序渲染?

Fixing Sequential XMLHttpRequest Calls in Vanilla JS

Hey there! I see you're struggling with getting sequential XMLHttpRequest calls working—let's break down what's going wrong and fix it step by step.

The Core Issue

Your current code fires off all XHR requests at once in the loop. Since they're asynchronous, responses can come back in any order (whichever the server sends first), and using innerHTML = output each time overwrites previous content instead of appending it. Splitting the logic into separate functions didn't fix this because you were still launching all requests in parallel, not waiting for one to finish before starting the next.


Solution 1: Classic Recursive Approach (Traditional Vanilla JS)

This method uses recursion to ensure each request only starts after the previous one completes. It's aligned with the classic XHR pattern you're working with:

function loadUniqueProjs(uniqueArray) {
  let currentIndex = 0;
  const accordionElement = document.getElementById('accordion');
  const spinnerElement = document.getElementById("spinner");

  // Recursive function to handle one request at a time
  function processNextRequest() {
    // Exit if we've processed all items
    if (currentIndex >= uniqueArray.length) {
      spinnerElement.hidden = true;
      console.log("All requests completed successfully!");
      return;
    }

    const arrayValue = uniqueArray[currentIndex];
    const xhr = new XMLHttpRequest();

    xhr.open('GET', `http://localhost:3000/resource/Projects/${arrayValue}`, true);
    
    xhr.onload = function() {
      if (this.status === 200) {
        const client = JSON.parse(this.responseText);
        let output = '';
        
        // Build the HTML for this response
        for (const key in client){
          output += `<div class="card">`+ 
          `<h5 class="card-header" role="tab" id="heading${key}">`+ 
          `<a data-toggle="collapse" data-parent="#accordion" style="color:black"` + 
          `href="#collapse${key}" aria-expanded="false" aria-controls="collapse${key}" class="d-block collapsed">` + 
          `<i class="fa fa-angle-double-down pull-right"></i>#${client[key].projectTitle} | ` + 
          `PM: ${client[key].projectManager} | ` + 
          `PO: ${client[key].projectOwner} | ` + 
          `Estimated Deadline: ${client[key].predictedCompletion} | ` + 
          `Status: ${client[key].status} | ` + 
          `Requestor: ${client[key].requestor}</a>`+ 
          `</h5>`+ 
          `</div>`;
        }
        
        // Append the new content instead of overwriting it
        accordionElement.innerHTML += output;
        console.log(`${this.status} Data retrieved successfully for item: ${arrayValue}`);
      } else {
        console.error(`Request failed for item ${arrayValue}, status code: ${this.status}`);
      }

      // Move to the next item in the array
      currentIndex++;
      processNextRequest();
    };

    // Handle network errors to avoid breaking the sequence
    xhr.onerror = function() {
      console.error(`Network error for item ${arrayValue}`);
      currentIndex++;
      processNextRequest();
    };

    xhr.send();
  }

  // Start the sequential request chain
  processNextRequest();
}

How This Works:

  1. We use a recursive function processNextRequest that only runs one XHR call at a time.
  2. After each request finishes (success or failure), we increment our index and call the function again to handle the next item.
  3. We use innerHTML += instead of innerHTML = to append new content, preserving the order of your array.

Solution 2: Promise + Async/Await (Modern Vanilla JS)

If you're open to using ES6+ features (still vanilla JS!), wrapping XHR in a Promise and using async/await makes the code cleaner and easier to read:

// Wrap XHR in a Promise for async/await compatibility
function makeXHRRequest(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    
    xhr.onload = function() {
      if (this.status === 200) {
        resolve(JSON.parse(this.responseText));
      } else {
        reject(new Error(`Request failed with status: ${this.status}`));
      }
    };
    
    xhr.onerror = function() {
      reject(new Error('Network error occurred'));
    };
    
    xhr.send();
  });
}

async function loadUniqueProjs(uniqueArray) {
  const accordionElement = document.getElementById('accordion');
  const spinnerElement = document.getElementById("spinner");

  try {
    for (const arrayValue of uniqueArray) {
      const url = `http://localhost:3000/resource/Projects/${arrayValue}`;
      const client = await makeXHRRequest(url);
      
      let output = '';
      for (const key in client){
        output += `<div class="card">`+ 
        `<h5 class="card-header" role="tab" id="heading${key}">`+ 
        `<a data-toggle="collapse" data-parent="#accordion" style="color:black"` + 
        `href="#collapse${key}" aria-expanded="false" aria-controls="collapse${key}" class="d-block collapsed">` + 
        `<i class="fa fa-angle-double-down pull-right"></i>#${client[key].projectTitle} | ` + 
        `PM: ${client[key].projectManager} | ` + 
        `PO: ${client[key].projectOwner} | ` + 
        `Estimated Deadline: ${client[key].predictedCompletion} | ` + 
        `Status: ${client[key].status} | ` + 
        `Requestor: ${client[key].requestor}</a>`+ 
        `</h5>`+ 
        `</div>`;
      }
      
      accordionElement.innerHTML += output;
      console.log(`Data retrieved successfully for item: ${arrayValue}`);
    }
  } catch (error) {
    console.error('Error during requests:', error);
  } finally {
    spinnerElement.hidden = true;
    console.log("All requests processed!");
  }
}

How This Works:

  1. makeXHRRequest converts the callback-based XHR into a Promise, which lets us use await.
  2. The async function loadUniqueProjs uses a for...of loop with await to pause execution until each request finishes before moving to the next.

Quick Notes on Your Original Code

  • Avoid using for...in to iterate over arrays (it can include unexpected enumerable properties). Use for loops, for...of, or array methods like forEach instead.
  • Always handle error cases (network failures, non-200 status codes) to prevent one failed request from breaking the entire sequence.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 05:24:55