在一系列异步XHR调用之后添加“回调”的最佳方法

时间:2010-02-05 16:41:09

标签: javascript jquery ajax asynchronous

我偶然发现了一段不是100%安全的Ajax代码,因为它混合了异步/同步类型的代码......所以基本上在下面的代码中我有一个jQuery.each,它在其中抓取元素和为每个启动Ajax get请求:

$(search).each(function() {
 $.ajax({
  url: 'save.x3?id='+$(this).attr("id")+'value='$(this).data("value");
  success: function(o){
   //Update UI
  },
  error: function(o){
   //Update UI
  }
 });
});

//code to do after saving...

很明显,“保存后要执行的代码......”通常会在所有请求完成之前执行。在理想的世界中,我希望服务器端代码能够同时处理所有这些代码并在保存成功回调后移动//代码,但假设这是不可能的,我将代码更改为类似这样的内容确保所有请求在继续之前回来,我仍然没有爱上:

var recs = [];
$(search).each(function() {
 recs[recs.length] = 'save.x3?id='+$(this).attr("id")+'value='$(this).data("value");
});

var counter = 0;
function saveRecords(){
 $.ajax({
  url: recs[counter],
  success: function(o){
   //Update progress
   if (counter<recs.length){
    counter++;
    saveRecords();
   }else{
    doneSavingRecords();
   }
  },
  error: function(o){
   //Update progress
   doneSavingRecords(o.status);
  }
 });
}

function doneSavingRecords(text){
 //code to do after saving...
}

if (recs.length>0){
 saveRecords();  //will recursively callback itself until a failed request or until all records were saved
}else{
 doneSavingRecords();
}

所以我正在寻找为一系列异步调用添加一些同步功能的“最佳”方式?

谢谢!

4 个答案:

答案 0 :(得分:2)

更好的答案

function saveRecords(callback, errorCallback){
  $('<div></div>').ajaxStop(function(){
    $(this).remove(); // Keep future AJAX events from effecting this
    callback();
  }).ajaxError(function(e, xhr, options, err){
    errorCallback(e, xhr, options, err);
  });

  $(search).each(function() {
    $.get('save.x3', { id: $(this).attr("id"), value: $(this).data("value") });
  });
}

将使用这样的:

saveRecords(function(){
   // Complete will fire after all requests have completed with a success or error
}, function(e, xhr, options, err){
   // Error will fire for every error
});

原始答案:如果他们需要按照特定顺序或您在页面上有其他常规AJAX事件会影响ajaxStop的使用,这是好的,但这会慢一点:

function saveRecords(callback){
  var recs = $(search).map(function(i, obj) {
   return { id: $(obj).attr("id"), value: $(obj).data("value") };
  });

  var save = function(){
   if(!recs.length) return callback();

   $.ajax({
    url: 'save.x3',
    data: recs.shift(), // shift removes/returns the first item in an array
    success: function(o){
     save();
    },
    error: function(o){
     //Update progress
     callback(o.status);
    }
   });
  }

  save();
}

然后你可以这样称呼它:

saveRecords(function(error){
   // This function will run on error or after all 
   // commands have run
});

答案 1 :(得分:1)

如果我理解您的要求,我认为您可以将$.ajaxStop()用于此目的。

答案 2 :(得分:1)

通过调用相同的函数来检查所有AJAX调用是否完整,可以轻松解决这个问题。你只需要在函数之间共享一个简单的队列,并快速检查(没有循环,定时器,承诺等)。

    //a list of URLs for which we'll make async requests
    var queue = ['/something.json', '/another.json']; 

    //will contain our return data so we can work with it
    //in our final unified callback ('displayAll' in this example)
    var data = [];

    //work through the queue, dispatch requests, check if complete
    function processQueue( queue ){
        for(var i = 0; i < queue.length; i++){
            $.getJSON( queue[i], function( returnData ) {
                data.push(returnData);

                //reduce the length of queue by 1
                //don't care what URL is discarded, only that length is -1
                queue.pop();

                checkIfLast(displayAll(data));
            }).fail(function() {
                throw new Error("Unable to fetch resource: " + queue[i]);
            });
        }
    }

    //see if this is last successful AJAX (when queue == 0 it is last)
    //if this is the last success, run the callback
    //otherwise don't do anything
    function checkIfLast(callback){
        if(queue.length == 0){
            callback();
        }
    }

    //when all the things are done
    function displayAll(things){
        console.log(things); //show all data after final ajax request has completed.
    }

    //begin
    processQueue();

编辑:我应该补充一点,我专门针对队列中的任意数量的项目。您可以简单地添加另一个URL,这将起到同样的作用。

答案 3 :(得分:0)

&GT;&GT;在理想的世界中,我希望服务器端代码能够同时处理所有这些代码并在保存成功回调后移动//代码

你需要在事件方面考虑这一点。 Closure的net.BulkLoader(或类似方法)将为您完成:

请参阅: goog.net.BulkLoader.prototype.handleSuccess_(针对个别电话) &安培; goog.net.BulkLoader.prototype.finishLoad_(完成所有通话)