Несколько вызовов ajax ждут последнего для загрузки, затем выполните

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

$.ajax({//ajax call 1
    url:page1.php,
    success: function(data1){
        //do something with data1
    }
});

....

$.ajax({//ajax call 2
    url:page2.php,
    success: function(data2){
        //do something with data2
    }
});
//consider more than just two concurrent requests

Скажем, что все запросы отправляются одновременно. Поскольку они асинхронны, они возвращаются в разное время. Скажем, один запрос принимает 100 мс для возврата, а другой запрос занимает 3000 мс для возврата. Я, очевидно, не знаю, какой из них будет возвращен первым или последним. Все они каким-то образом обновляют DOM, и я хочу, чтобы эти изменения отображались в средстве просмотра одновременно за одно обновление. Как это сделать?

Самое лучшее, о чем я могу думать, - сохранить данные1 и data2 в качестве глобальных переменных. А затем есть переменная счетчика, которая учитывает каждый раз, когда возвращается успех. Затем, на счетчике == TOTAL_NUM_REQUESTS вызывается функция типа updateAll(), затем проходит через все глобальные переменные и помещает их туда, куда им нужно идти. Но это кажется грязным и подверженным ошибкам. Плюс к этому было бы много глобальных переменных.

В идеале я хочу, чтобы обработчики успеха спали по возвращении, а затем при моем состоянии подсчета всех из них, как они были возвращены, отправляйте сообщения о пробуждении всем им, и они могут возобновить выполнение. Это кажется самым чистым, но я не знаю о такой функциональности, как это в javascriptland.

Есть ли у кого-нибудь интересные идеи для этого?

ANSWER UPDATE

Спасибо Ли ниже, я смог получить решение. Мое решение было похоже на его ниже. Я создаю список переменных async, которые были назначены вызову $.ajax. Изначально обработчик успеха этих вызовов ajax все еще вызывался, поэтому я удалил их и поместил их в другую функцию, которую впоследствии должен был вызвать этот блок when, который я написал. Я передал results в функцию, подобную этой:

var results = [];
results.push(async1);
results.push(async2);
... for all the results ...

$.when.apply(this, results).done(function() {
   for(var i=0;i<arguments.length;i++){
     dataobject=arguments[i][0]
     if(dataobject.variousattribute)
       mySuccessHandlerFirstGuy(dataobject)
     else if(dataobject.anotherattribute)
       mySuccessHandlerSecondGuy(dataobject)
       //etc ....
   }
};

Объект arguments занял немного работы, чтобы выяснить, что это было. Это был массив 2d (список списков). Первый индекс представлял возвращаемый объект, соответствующий заданному запросу ajax. Кажется, что все в порядке, но было бы лучше, если бы ваш сервер вернул то, что вы можете искать, и напишите блок if/else соответственно. Затем в этом элементе в этом списке есть 3 элемента. Первый - это значение, которое было возвращено с сервера, то есть то, что вы хотите. Вторая всегда была строкой success, которую вы, вероятно, можете использовать, чтобы проверить, работал ли вызов. И третий элемент в этом списке, по-видимому, является первоначальным запросом (хотя я не уверен). Это было бесполезно для меня.

В любом случае, я надеюсь, что это поможет кому-то в будущем. И еще раз спасибо Ли, чтобы указать мне в правильном направлении.

Ответы

Ответ 1

Используйте функцию jQuery $.when(), чтобы запустить что-то, когда закончились все вызовы ajax:

jQuery.when docs

var async1 = $.ajax({//ajax call 1
    url:page1.php,
    success: function(data1){
        //do something with data1
    }
});

....

var async2 = $.ajax({//ajax call 2
    url:page2.php,
    success: function(data2){
        //do something with data2
    }
});

$.when(async2, async1).done(function(result2, result1) {
    ... do this when both are successful ...
});

Добавлено в ответ на вопросы:

Если у вас есть куча вызовов ajax, вы можете использовать "apply" следующим образом:

var results = [];
results.push(async1);
results.push(async2);
... for all the results ...

$.when.apply(this, results).done(function() {
    ... use 'arguments' array to get results ...
});

Ответ 2

Возможно, это будет сделано очень похоже на то, что вы уже предложили.

Сначала создайте глобальные переменные var sleep = true и var request_count = 0. Затем запустите таймер, который проверяет request_count на общее количество запросов каждую секунду или около того. Затем ваши функции успеха могут увеличивать request_counter, а затем начинать цикл до sleep == false. Затем, когда функция мониторинга, выполняемая с помощью таймера, обнаруживает request_count == TOTAL_REQUESTS, она устанавливает sleep = false, и все ваши функции успеха продолжают об их бизнесе в этой точке.

Ответ 3

Простым решением будет использование модуля PreloaderQ

https://www.npmjs.com/package/preloaderq

Используйте как ниже

var preloader = new PreloaderQ()
preloader.setEmptyCallback(function(){
    //all tasks are finished
})

preloader.setFirstTaskCallback(function(){
    //started first task
})

preloader.enqueueTask('ajax1')

$.ajax({//ajax call 1
    url:page1.php,
    success: function(data1){
        prealoader.dequeueTask('ajax1')
        //do something with data1
    }
});

preloader.enqueueTask('ajax2')

$.ajax({//ajax call 1
    url:page2.php,
    success: function(data2){
        prealoader.dequeueTask('ajax2')
        //do something with data2
    }
});

Вызов emptycallback вызывается после того, как оба завершены