如何使所有AJAX调用都是顺序的?

How to make all AJAX calls sequential?

提问人:Jader Dias 提问时间:7/20/2009 最后编辑:Jader Dias 更新时间:4/21/2022 访问量:45778

问:

我使用jQuery。而且我不希望在我的应用程序上进行并行 AJAX 调用,每次调用都必须等待前一次调用才能开始。如何实现?有帮手吗?

更新如果有任何 XMLHttpRequest 的同步版本或 jQuery.post 我想知道。但是顺序!=同步,我想要一个异步和顺序的解决方案。

jquery ajax 并行处理 jquery-deferred 顺序

评论

4赞 Ed S. 7/20/2009
您确实知道 AJAX 中的“A”代表什么,对吧:-)
2赞 Sam Harwell 7/20/2009
这绝对不是一个答案,但当这是一个令人困惑的问题而不是如何使其并发时,这是一个精心设计的框架的有趣迹象。:)
8赞 Nosredna 7/20/2009
@Ed Swangren,是的,但 AJAX 不一定是异步的、JavaScript 或 XML。:-)
0赞 Nosredna 7/20/2009
@280Z28,谢天谢地。我记得当我拥有 Amiga、DOS 和 Mac 用户时,他们总是告诉我人们不需要多任务处理。
1赞 Jader Dias 7/20/2009
@Nosredna 但是 AJAX 怎么可能不是 Javascript?

答:

6赞 Nosredna 7/20/2009 #1

你能想到两个选择。一种是通过回调来链接它们。另一种是使调用同步而不是异步。

您希望它们按顺序排列是有原因的吗?这会减慢速度。

若要使调用同步,请将 Ajax 调用中的 async 选项设置为 false。请参阅 http://docs.jquery.com/Ajax/jQuery.ajax#options 上的文档(单击“选项”选项卡查看它们)。

评论

0赞 Jader Dias 7/20/2009
如何使它们同步?
0赞 Jader Dias 7/20/2009
或者第三种选择:一个类,它将管理一个请求队列,并按顺序处理它们。
0赞 Nosredna 7/20/2009
我编辑了我的答案以指向 Ajax 选项。将 async 选项设置为 false。
0赞 Nosredna 7/20/2009
是的。您可以将 Ajax 回调转到启动下一个请求的处理程序。我想这可能比手动链接回调更容易处理。
0赞 Paul Flahr 10/7/2020
从 jQuery 1.8 开始,将 AJAX async 设置为 false 已弃用
0赞 FWH 7/20/2009 #2

看看这个: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。

评论

0赞 Jader Dias 7/20/2009
我知道您的解决方案,但是多个请求不是由同一个请求生成的,因此我无法像您所示将它们链接在一起。我的解决方案更像是 C 中的锁(globalAjaxObject)#
1赞 Gab Royer 7/20/2009 #3

最好的方法是像 Nosredna 所说的那样链接回调。我不建议使用同步 XMLHttpRequest,因为它们会锁定您的整个应用程序。

据我所知,没有太多的帮助,但你可以做一些类似于回调FIFO的事情。

1赞 Breton 7/20/2009 #4

您可以尝试使用叙述性 javascript http://www.neilmix.com/narrativejs/doc/

不过我自己从来没有用过它。如果我想这样做,我会设置某种抽象来链接异步操作。正如其他人所说,ajax 对象的同步版本在等待响应时会阻止事件被处理。这会导致浏览器看起来像是冻结的,直到它收到响应。

评论

0赞 Jader Dias 7/20/2009
感谢您的添加,但我不确定它是否解决了我的问题。如果只有一个生成线程,它会使异步调用按顺序进行,但在我的情况下,许多事件会生成请求。
0赞 Breton 7/20/2009
就像我说的,我没有使用过 NJS,但它的描述说它添加了一个 yield 运算符,它只是停止函数的执行,直到事件触发。在我看来,这不会阻止并发。如果是这样,它对任何人来说都不是一个非常有用的库。
0赞 Breton 7/20/2009
如果不出意外,你可以研究编译后的代码,你可能会找到一个简单的JS解决方案来解决你的问题。
1赞 Joe Chung 7/20/2009 #5

async 选项设置为 false,例如,

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

参考:Ajax/jQuery.ajax

评论

1赞 Mr. Lance E Sloan 12/5/2012
async 选项将很快从 jQuery 中删除。
0赞 kamelkev 6/18/2016
这是对OP最初请求的最佳答案。此建议保证了 IOE(按顺序执行)的复杂性最低。从来没有计划从 jquery 中删除异步标志,这与@LS暗示的相反。
2赞 Mr. Lance E Sloan 6/18/2016
请记住:“从 jQuery 1.8 开始,将 async: false 与 jqXHR ($.Deferred) 已弃用;您必须使用 success/error/complete 回调选项,而不是 jqXHR 对象的相应方法,例如 jqXHR.done()。docs.jquery.com/Ajax/jQuery.ajax#options
0赞 kamelkev 6/19/2016
@LS 弃用并不意味着删除。您可以在讨论此问题的原始票证上阅读更多相关信息:bugs.jquery.com/ticket/11013
-2赞 tomski777 6/21/2010 #6

同步调用不一定更慢,如果你有一个应用程序,其中 AJAX 调用打开、发布到,然后关闭套接字,则对套接字的多次调用没有意义,因为某些套接字只能处理单个连接,在这种情况下,排队数据,使其仅在上一个 AJAX 调用完成时发送意味着更高的数据吞吐量。

20赞 Todd Chaffee 9/27/2012 #7

有一种比使用同步 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的承诺。我很确定 promise 很快就会成为原生的 javascript 语言功能,所以这应该会给你一个很好的学习动力。

评论

1赞 dan-lee 6/1/2013
嘿,这有点晚了,但无论如何:很棒的答案帮助了我,就像一个魅力。但我真的不明白它是如何工作的。我试图理解和阅读文档,但这对我来说是一个谜。
1赞 MD. Sahib Bin Mahboob 5/21/2014
@DanLee 看来你不是唯一一个。
0赞 Todd Chaffee 5/22/2014
很难理解 javascript 承诺以及 jquery 在不学习的情况下实现概念的方式;那里有很多文档。
0赞 Todd Chaffee 5/22/2014
我编辑了答案,以更清楚地说明您需要什么作为理解代码的背景。
2赞 Todd Chaffee 11/15/2016
@kamelkev,这并不完全正确。设置将防止代码中的其他事件在等待请求返回时触发。强烈建议不要这样做,因为即使是点击等浏览器事件也不会被触发。我提供的代码允许 aysnc 请求仍然以异步方式运行,但按严格的顺序运行。async: false
-2赞 tsknakamura 7/5/2015 #8

如何使用 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);
-4赞 kamelkev 6/18/2016 #9

sequential != synchronous,我想要一个异步和顺序的解决方案

同步执行通常意味着“使用相同的时钟”,而顺序执行意味着“按顺序或顺序跟随”。

对于您的特定用例,我认为必须满足这两个条件,因为异步执行意味着非顺序结果的可能性。

5赞 Rm558 1/13/2018 #10
(async () => { 
  for(f of ['1.json','2.json','3.json']){
    var json = await $.getJSON(f);
    console.log(json)
 };
})()
  1. 使用 jQuery ajax 调用请求 3 个 json 文件
  2. 使用 await 按顺序(不并行)处理
  3. 适用于 Chrome/Firefox/Edge(截至 2018 年 1 月 30 日)

更多内容请见 MDN

1赞 Muthu Kumar 5/12/2019 #11

您可以使用 promise 使 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);

评论

0赞 Muthu Kumar 1/1/2021
有一个 jquery 插件可以发出顺序的 ajax 请求 foliotek.github.io/AjaxQ
2赞 jfriend00 4/20/2022 #12

对 jQuery 异步操作进行排序的现代方法是使用它们已经返回的 promise 和 promise 支持的流控制,而这在前几年的任何其他答案中目前都没有显示。

例如,假设您想加载多个脚本,但这些脚本必须按顺序加载,因此第二个脚本在第一个脚本完成之前不会加载/运行,依此类推,您想知道它们何时全部完成。您可以直接使用已经返回的 promise。为简单起见,您可以在这样的循环中做出承诺:$.getScript()$.getScript()awaitfor

async function loadScripts(scriptsToLoad) {
    for (const src of scriptsToLoad) {
        await $.getScript(src);
    }
}

loadScripts([url1, url2, url3]).then(() => {
    console.log("all done loading scripts");
}).catch(err => {
    console.log(err);
});

由于所有与 jQuery Ajax 相关的异步操作现在都返回 promise(并且已经很多年了),您可以将此概念扩展到任何与 jQuery 的 Ajax 相关的操作。


另外,请注意,此处其他答案中将 jQuery 操作包装在新 promise 或延迟的 jQuery 中的所有其他尝试都已过时,并被视为 promise 反模式,因为当操作本身已经返回 promise 时,您可以直接使用该 promise,而无需尝试将其包装在自己的新 promise 中。

评论

0赞 tobybot 3/3/2023
好干净的例子展示了它的用途,这才是人们应该关注的!await