Javascript - XMLHttpRequest如何发送多个并行请求?

3

我遇到了一个非常神秘的问题。我正在尝试实现一个购物车,通过在客户端存储cookie来标识已订购商品的ID和数量。当我加载结算HTML页面时,我会读取cookie并逐个获取项目ID;然后对于每个项目ID,我都会向我的servlet发送请求以返回信息。为了简单起见,我已经删除了冗余行:

 var arrXhr = new Array();
 function ProcessCheckout(){
        try
        {
            var myArr = new Array();
            myArr[0]=84234;
            myArr[1]=84239;
            myArr[2]=84240;

            for (var intLoop=0; intLoop < myArr.length; intLoop++){
                var intIcint=myArr[intLoop]; 

                arrXhr[intIcint]= new XMLHttpRequest();
                function Populate_CheckOutCart(){
                arrXhr[intIcint].open('POST', strRemoteUriReq, true);                         
                arrXhr[intIcint].send(null);                                       
                arrXhr[intIcint].onreadystatechange= ProcessSrvRsp(intIcint);
            }
        }catch(errors)
           {
              alert(errors.message);
           }
     }
 }

       function ProcessSrvRsp(ItemToProcess){
            if (arrXhr[ItemToProcess].readyState==4){   
                //doing some functionality here on item code: ItemToProcess
             }  
       }

这里的问题在于第

行。

arrXhr[intIcint].open('POST', strRemoteUriReq, true);  

如果我把请求类型从“同步”通信更改为“异步”,即从“TRUE”更改为“FALSE”,则一切都可以正常工作,但是您知道网页将不得不等待服务器处理每个项目。因此,网页将发送一个项目84234的请求,等待响应后再发送一个项目84239的请求,以此类推。
据我的了解,如果我改回使用“异步”XMLHttpRequest,则只有在arrXhr [ItemToProcess] .readyState == 1时才会发生任何事情。但是对于其他状态,2,3,4(4是最重要的开始工作的),它们从未被触发。
有什么想法吗?最重要的是我们如何克服这个问题?我知道XMLHttpRequest在单独的线程上工作,很可能这是问题所在,但我想不出解决方案。
我的目标很简单,我希望我的网页在从磁盘上的cookie读取时立即向我的servlet发送多个请求;因此,对于每个cookie,我都想发送一个请求,并希望以异步方式接收响应。我不希望浏览器等待直到完成最终请求。因此,如果您有任何其他想法/实现方法,我可以是一个非常开放的人;)
附言:我正在使用TomCat 7

2
只是一条评论:为什么不单独发送所有订购商品的请求? - Andrew D.
4个回答

4

类似于下面的内容:

function ProcessCheckout() {
    var arrXhr=[];
    var myArr=[];
    myArr.push(84234);
    myArr.push(84239);
    myArr.push(84240);

    var helperFunc=function(arrIndex,itemId) {
      return function() {
        if(arrXhr[arrIndex].readyState===4) {
          //doing some functionality here on item
          ProcessResponseForItem(arrIndex,myArr,arrXhr);
          // doing some code if all xhr's is completed
          ProcessResponseForAllItems(myArr,arrXhr);
        }
      }
    }

    for(var i=0; i<myArr.length; i++) {
      var itemId=myArr[i]; 
      arrXhr[i]=new XMLHttpRequest();
      arrXhr[i].open('POST', strRemoteUriReq, true);
      arrXhr[i].onreadystatechange=helperFunc(i,itemId);
      arrXhr[i].send(/*some item data sended to server for item with id itemId*/);
    }
 }

 function ProcessResponseForItem(arrIndex,myArr,arrXhr) {
   if(arrXhr[arrIndex].status===200) {
     // do some code if response is succ
   }
   else {
     // if fail
   }
 }

 function ProcessResponseForAllItems(myArr,arrXhr) {
   var i,isAllComplete=true,isAllCompleteSucc=true;
   for(i=0;i<myArr.length;i++) if((!arrXhr[i])||(arrXhr[i].readyState!==4)) {
     isAllComplete=false;
     break;
   }
   if(isAllComplete) {
     for(i=0;i<myArr.length;i++) if(arrXhr[i].readyState!==200) {
       isAllCompleteSucc=false;
       break;
     }
     if(isAllCompleteSucc) {
       // do some code when all is completed and all is succ
     }
     else {
       // do some code when all is completed and some is fail
     }
   }
 }

问题:您编写的下面的代码很重要吗?arrXhr[i].send(/ 发送到具有itemId的项目的某些项目数据的服务器 /); 我的意思是send()中的参数有什么用处?它们能帮助解决这个问题吗? - deadCrow
上面的代码是一个简单的例子,展示了如何将多个异步请求合并为一个。当你在代码中使用异步模式时,主要问题在于:.onreadystatechange= ProcessSrvRsp(intIcint) 这一行没有任何意义。.onreadystatechange=FunctionObject; 必须放在 .send 之前。而且,.send(null) 不会向服务器发送任何数据。 - Andrew D.
  1. 需要使用.onreadystatechange来设置一个函数指针(例如:function xxx(){}; xhr.onreadystatechange=xxx;);2) 第二个问题是你需要将参数保留值发送到函数xxx中;3) 我的helperFunc解决了这些问题:helperFunc将一些值作为参数传递以在闭包中保留,创建一个新函数,该函数可以看到保留的参数,并返回此新函数以存储到.readystatechange中。
- Andrew D.
1
阅读有关JavaScript闭包的内容:https://developer.mozilla.org/en/JavaScript/Guide/Closures,以及有关.readyStateChange的内容https://developer.mozilla.org/En/XMLHttpRequest/Using_XMLHttpRequest。 - Andrew D.
1
有多种实现多个请求的方法。您可以使用一个变量,但在不同的闭包中创建多个xhr。您可以使用xhr对象数组。这并不重要。但是您不能仅使用一个xhr处理所有请求。 - Andrew D.
显示剩余4条评论

1
我强烈建议使用事件监听器,以便在使用异步处理时使用。请注意,Gecko 30.0(Firefox 30.0等)已弃用同步请求,因为对用户体验有负面影响。当然,这(理解事件监听器)是异步的,并且在到达时立即处理响应。到目前为止,onreadystatechange可能会带来许多复杂性和头痛。特别是当您处理多个响应并且响应处理顺序很重要时,情况就更加如此。当您想要按照到达的方式处理响应时,有一种简单的方法可以向每个请求添加EventListener。
arrXhr[intIcint].open('POST', strRemoteUriReq, true);                         
arrXhr[intIcint].addEventListener("load", processResponse);
arrXhr[intIcint].send(null);                                       

...

function processResponse() {
  console.log("Response arrived.");
}

你甚至可以自定义要处理的状态。以下是当前允许的响应状态:
arrXhr[intIcint].addEventListener("progress", updateProgress);
arrXhr[intIcint].addEventListener("load", transferComplete);
arrXhr[intIcint].addEventListener("error", transferFailed);
arrXhr[intIcint].addEventListener("abort", transferCanceled);

1
你必须使用“this”代替arrXhr[intIcint]。
var arrXhr = new Array();
 function ProcessCheckout(){
    try
        {
            var myArr = new Array();
            myArr[0]=84234;
            myArr[1]=84239;
            myArr[2]=84240;

            for (var intLoop=0; intLoop < myArr.length; intLoop++){
                var intIcint=myArr[intLoop]; 

                xhr= new XMLHttpRequest();
                function Populate_CheckOutCart(){
                xhr.open('POST', strRemoteUriReq, true);                         
                xhr.send(null);                                       
                xhr.onreadystatechange= ProcessSrvRsp();
                // or   xhr.addEventListener("load", ProcessSrvRsp);

            }
        }catch(errors)
           {
              alert(errors.message);
           }
     }
 }

    function ProcessSrvRsp(){
        if (this.readyState==4){   
            //doing some functionality here on item code: this
        }  
   }

0

不要为每个产品ID发送多个同时请求,而是将所有ID包装成一个数组,序列化它并仅使用一个请求。

var ids = [1, 2, 3];
var serializedIds = ids.join('|'); // serializedIds = '1|2|3';

只通过请求发送serializedIds变量


嗨,弗拉德,我不确定你的意思是什么。但我怀疑你想让我聚合请求并将它们作为一个发送。问题在于远程服务器可能需要很长时间来处理50或150个项目,并将它们作为一个块发送回来。此外,我不确定HTTP或Servlet在响应客户端请求时向客户端传输多少数据的限制。话虽如此,如果只有3个同时请求,您的建议是理想的。但是,想象一下我们正在查询100个项目... - deadCrow
将50或150个项目分为50或150块进行处理会慢得多。 - Stephan Bijzitter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接