Как сделать все вызовы AJAX последовательными?

Я использую jQuery. И я не хочу параллельных вызовов AJAX в своем приложении, каждый вызов должен ждать предыдущего перед запуском. Как это реализовать? Есть ли помощник?

ОБНОВЛЕНИЕ Если есть какая-либо синхронная версия XMLHttpRequest или jQuery.post, я хотел бы знать. Но последовательный != синхронный, и я хотел бы асинхронное и последовательное решение.


person Jader Dias    schedule 20.07.2009    source источник
comment
Вы знаете, что означает буква A в AJAX, верно :-)   -  person Ed S.    schedule 20.07.2009
comment
Это определенно не ответ, но это интересный признак хорошо спроектированной структуры, когда это является запутанным вопросом, а не тем, как сделать его параллельным. :)   -  person Sam Harwell    schedule 20.07.2009
comment
@ Эд Свангрен, да, но AJAX не обязательно должен быть асинхронным, JavaScript или XML. :-)   -  person Nosredna    schedule 20.07.2009
comment
@ 280Z28, слава богу. Я помню, когда у меня была Amiga, пользователи DOS и Mac всегда говорили мне, что людям не нужна многозадачность.   -  person Nosredna    schedule 20.07.2009
comment
@Ed Я понимаю, что A для асинхронного. Но у каждого асинхронного метода может быть синхронная версия. Я не знаю, реализована ли уже где-то для AJAX эта синхронная версия.   -  person Jader Dias    schedule 20.07.2009
comment
@Nosredna Но как AJAX может не быть Javascript?   -  person Jader Dias    schedule 20.07.2009


Ответы (11)


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

// How to force async functions to execute sequentially 
// by using deferred pipe chaining.

// The master deferred.
var dfd = $.Deferred(),  // Master deferred
    dfdNext = dfd; // Next deferred in the chain
    x = 0, // Loop index
    values = [], 

    // Simulates $.ajax, but with predictable behaviour.
    // You only need to understand that higher 'value' param 
    // will finish earlier.
    simulateAjax = function (value) {
        var dfdAjax = $.Deferred();

        setTimeout(
            function () {
                dfdAjax.resolve(value);
            },
            1000 - (value * 100)
        );

        return dfdAjax.promise();
    },

    // This would be a user function that makes an ajax request.
    // In normal code you'd be using $.ajax instead of simulateAjax.
    requestAjax = function (value) {
        return simulateAjax(value);
    };

// Start the pipe chain.  You should be able to do 
// this anywhere in the program, even
// at the end,and it should still give the same results.
dfd.resolve();

// Deferred pipe chaining.
// What you want to note here is that an new 
// ajax call will not start until the previous
// ajax call is completely finished.
for (x = 1; x <= 4; x++) {

    values.push(x);

    dfdNext = dfdNext.pipe(function () {
        var value = values.shift();
        return requestAjax(value).
            done(function(response) {
                // Process the response here.

            });

    });

}

Некоторые люди прокомментировали, что понятия не имеют, что делает код. Чтобы понять это, вам сначала нужно понять обещания javascript. Я почти уверен, что обещания скоро станут нативной функцией языка javascript, так что это должно дать вам хороший стимул учиться.

person Todd Chaffee    schedule 26.09.2012
comment
Эй, это немного поздно, но в любом случае: отличный ответ помог мне и работает как шарм. Но я действительно не понимаю, как это работает. Я пытался понять и прочитать документы, но это для меня загадка. - person dan-lee; 01.06.2013
comment
@DanLee Похоже, ты не один такой. - person MD. Sahib Bin Mahboob; 21.05.2014
comment
Трудно понять обещания javascript и то, как jquery реализует концепцию, не изучая ее ;-) Существует множество документов. - person Todd Chaffee; 21.05.2014
comment
Я отредактировал ответ, чтобы было понятнее, что вам нужно в качестве фона для понимания кода. - person Todd Chaffee; 21.05.2014
comment
Вы, по сути, отключаете асинхронный характер запроса ajax, используя этот шаблон, который можно аналогичным образом выполнить, просто установив async: false в качестве аргумента вашего запроса. Это вызывает точно такое же поведение, как то, что вы создаете с помощью своего предложения. - person kamelkev; 18.06.2016
comment
@kamelkev, это не совсем правильно. Установка async: false предотвратит запуск других событий в коде во время ожидания возврата запроса. Это крайне не рекомендуется, потому что даже такие события браузера, как клики, не будут инициированы. Код, который я предоставил, позволяет запросам aysnc по-прежнему выполняться асинхронно, но в строгом последовательном порядке. - person Todd Chaffee; 15.11.2016

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

Есть ли причина, по которой вы хотите, чтобы они были последовательными? Это замедлит ход событий.

Чтобы сделать вызов синхронным, вы установите для параметра async в вызове Ajax значение false. См. документацию по адресу http://docs.jquery.com/Ajax/jQuery.ajax#options (щелкните вкладку параметров, чтобы увидеть их).

person Nosredna    schedule 20.07.2009
comment
Как сделать их синхронными? - person Jader Dias; 20.07.2009
comment
Или третий вариант: класс, который будет управлять очередью запросов и последовательно их обрабатывать. - person Jader Dias; 20.07.2009
comment
Я отредактировал свой ответ, указав на параметры Ajax. Установите для параметра асинхронности значение false. - person Nosredna; 20.07.2009
comment
Да. Вы можете настроить обратный вызов Ajax на обработчик, который запускает следующий запрос. Я предполагаю, что с этим может быть проще справиться, чем вручную связывать обратные вызовы. - person Nosredna; 20.07.2009
comment
Начиная с jQuery 1.8, установка AJAX async на false устарела. - person Paul Flahr; 06.10.2020

Лучший способ сделать это - связать обратные вызовы, как сказал Носредна. Я бы не рекомендовал использовать синхронный XMLHttpRequest, поскольку они блокируют все ваше приложение.

Насколько я знаю, для этого не так много помощников, но вы можете сделать что-то похожее на callback FIFO.

person Gab Royer    schedule 20.07.2009

Вы можете попробовать повествовательный JavaScript http://www.neilmix.com/narrativejs/doc/

Я никогда не использовал его сам, хотя. Если бы я хотел сделать это, я бы установил какую-то абстракцию для объединения асинхронных действий. Как уже говорили другие, синхронная версия объекта ajax блокирует обработку событий, пока она ожидает ответа. Это заставляет браузер выглядеть так, как будто он завис, пока не получит ответ.

person Breton    schedule 20.07.2009
comment
Спасибо за дополнение, но я не уверен, что оно решает мою проблему. Он делает асинхронные вызовы последовательными, если был только один генерирующий поток, но в моем случае многие события порождают заявки. - person Jader Dias; 20.07.2009
comment
Как я уже сказал, я не использовал NJS, но в его описании сказано, что он добавляет оператор yield, который просто останавливает выполнение функции до тех пор, пока не сработает событие. Мне не кажется, что это предотвратит параллелизм. Если бы это было так, это не была бы очень полезная библиотека для кого-либо. - person Breton; 20.07.2009
comment
Если ничего другого, вы можете изучить скомпилированный код, и таким образом вы можете найти простое решение JS для вашей проблемы. - person Breton; 20.07.2009

Установите для параметра async значение false, например,

$.ajax({ async: false /*, your_other_ajax_options_here */ });

Ссылка: Ajax/jQuery.ajax

person Joe Chung    schedule 20.07.2009
comment
Опция async скоро будет удалена из jQuery. - person Mr. Lance E Sloan; 05.12.2012
comment
Это лучший ответ на исходный запрос ОП. Это предложение гарантирует IOE (выполнение по порядку) с наименьшей сложностью. Никогда не планировалось удалять асинхронный флаг из jquery, вопреки тому, что подразумевает @LS. - person kamelkev; 18.06.2016
comment
Помните: начиная с jQuery 1.8 использование async: false с jqXHR ($.Deferred) не рекомендуется; вы должны использовать параметры успешного/ошибочного/полного обратного вызова вместо соответствующих методов объекта jqXHR, таких как jqXHR.done(). docs.jquery.com/Ajax/jQuery.ajax#options - person Mr. Lance E Sloan; 18.06.2016
comment
@LS Устаревший не означает удалить. Вы можете прочитать больше об этом в оригинальной заявке, где это обсуждалось: bugs.jquery.com/ticket/11013 - person kamelkev; 19.06.2016

Посмотрите на это: http://docs.jquery.com/Ajax/jQuery.ajax (перейдите на вкладку "Параметры").

Но помните, что синхронный вызов заморозит страницу до тех пор, пока не будет получен ответ, поэтому его нельзя использовать на рабочем сайте, потому что пользователи разозлятся, если по какой-либо причине им придется ждать 30 секунд с зависшим браузером.

РЕДАКТИРОВАТЬ: хорошо, с вашим обновлением становится яснее, чего вы хотите достичь;)

Итак, ваш код может выглядеть так:

    $.getJSON("http://example.com/jsoncall", function(data) {
        process(data);
        $.getJSON("http://example.com/jsoncall2", function (data) {
            processAgain(data);
            $.getJSON("http://example.com/anotherjsoncall", function(data) {
                processAgainAndAgain(data);
            });
        });
    });

Таким образом, второй вызов будет выполняться только после получения и обработки ответа на первый вызов, а третий вызов будет выполняться только после получения и обработки ответа на второй вызов. Этот код предназначен для getJSON, но его можно адаптировать к $.ajax.

person FWH    schedule 20.07.2009
comment
Я знаю о вашем решении, но множественные запросы не генерируются одним и тем же, поэтому я не могу связать их вместе, как вы показали. Мое решение было бы больше похоже на блокировку (globalAjaxObject) в С#. - person Jader Dias; 20.07.2009

(async () => { 
  for(f of ['1.json','2.json','3.json']){
    var json = await $.getJSON(f);
    console.log(json)
 };
})()
  1. запрашивает 3 файла json с вызовами jQuery ajax
  2. процесс последовательно (не параллельно) с ожиданием
  3. работает в Chrome/Firefox/Edge (по состоянию на 30.01.2018)

подробнее на MDN

person Rm558    schedule 12.01.2018

Вы можете использовать обещание, чтобы сделать вызовы ajax последовательными. Используя метод Array push и pop promise, последовательные вызовы ajax будут намного проще.

var promises = [Promise.resolve()];

function methodThatReturnsAPromise(id) {
  return new Promise((resolve, reject) => {
    $.ajax({
      url: 'https://jsonplaceholder.typicode.com/todos/'+id,
      dataType:'json',
      success: function(data)
      {
        console.log("Ajax Request Id"+id);
        console.log(data);
        resolve();
      }
    });
  });
} 

function pushPromise(id)
{
  promises.push(promises.pop().then(function(){
    return methodThatReturnsAPromise(id)}));
}


pushPromise(1);
pushPromise(3);
pushPromise(2);
person Muthu Kumar    schedule 12.05.2019
comment
Существует подключаемый модуль jquery для выполнения последовательных запросов ajax foliotek.github.io/AjaxQ. - person Muthu Kumar; 01.01.2021

Синхронные вызовы не обязательно медленнее, если у вас есть приложение, в котором вызовы AJAX открываются, отправляются в сокет и затем закрываются, множественные вызовы к сокету не имеют смысла, поскольку некоторые сокеты могут обрабатывать только одно соединение, и в этом случае данные в очереди, поэтому они отправляются только после завершения предыдущего вызова AJAX, что означает гораздо более высокую пропускную способность данных.

person tomski777    schedule 21.06.2010

Как насчет использования событий Node.js?

var EventEmitter = require('events').EventEmitter;
var eventEmitter = new EventEmitter();
var $ = require('jquery');

var doSomething = function (responseData) {
  var nextRequestData = {};
  // do something with responseData
  return nextRequestData;
};

// ajax requests
var request1 = $.ajax;
var request2 = $.ajax;
var requests = [request1, request2];

eventEmitter.on('next', function (i, requestData) {
  requests[i](requestData).then(
    function (responseData) {
      console.log(i, 'request completed');
      if (i+1 < requests.length) {
        var nextRequestData = doSomething(responseData);
        eventEmitter.emit('next', i+1, nextRequestData);
      }
      else {
        console.log('completed all requests');
      }
    },
    function () {
      console.log(i, 'request failed');
    }
  );
});

var data = {
  //data to send with request 1
};
eventEmitter.emit('next', 0, data);
person tsknakamura    schedule 05.07.2015

последовательный != синхронный, и я хотел бы асинхронное и последовательное решение

Синхронное выполнение обычно означает «использование одних и тех же часов», а последовательное выполнение означает «следование порядку или последовательности».

Я думаю, что для вашего конкретного случая использования должны быть выполнены оба условия, поскольку асинхронное выполнение подразумевает возможность непоследовательного результата.

person kamelkev    schedule 18.06.2016