Лучший способ добавить «обратный вызов» после серии асинхронных вызовов XHR - PullRequest
1 голос
/ 05 февраля 2010

Я наткнулся на кусок кода Ajax, который не на 100% безопасен, так как он смешивает асинхронный / синхронный тип кода ... так что в основном в приведенном ниже коде у меня есть jQuery.each, в котором он получает информацию об элементах и Запустите запрос Ajax для каждого:

$(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 ]

2 голосов
/ 05 февраля 2010

Лучший ответ :

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 голос
/ 27 сентября 2013

Это легко решить, вызвав ту же функцию, чтобы проверить, что все вызовы 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, и он будет работать точно так же.

1 голос
/ 05 февраля 2010

Если я понимаю, о чем вы спрашиваете, я думаю, вы можете использовать $. AjaxStop () для этой цели.

0 голосов
/ 05 февраля 2010

>> В идеальном мире я хотел бы, чтобы серверный код обрабатывал их все сразу и перемещал // код для выполнения после сохранения в обратном вызове успеха

Вам нужно будет подумать об этом с точки зрения событий.NetBulkLoader от Closure (или аналогичный подход) сделает это за вас:

См .: goog.net.BulkLoader.prototype.handleSuccess_ (для индивидуальных вызовов) & goog.net.BulkLoader.prototype.finishLoad_ (для завершения всех вызовов)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...