在脚本执行过程中,当AJAX调用返回时,JavaScript中会发生什么

What happens in JavaScript when an AJAX call returns while the script is executing?

本文关键字:JavaScript 什么 调用 执行 脚本 过程中 AJAX 返回      更新时间:2023-09-26

假设我编写了一些JavaScript,使用myCallback作为AJAX调用的回调方法,在AJAX成功时执行。

假设当myCallback被异步调用时,在我的页面上调用了其他一些名为myFunction的JavaScript方法。

一个操作是否优先于另一个操作?它们会同时运行吗?会发生什么呢?

假设当myCallback被异步调用时,在我的页面上调用了其他一些称为myFunction的JavaScript方法。

一个操作是否优先于另一个操作?它们会同时运行吗?会发生什么呢?

浏览器上的JavaScript是单线程的(禁止你使用web worker,其语法是显式的)。所以myFunction将运行,直到它返回-与某些警告(继续阅读)。如果ajax层在 myFunction运行时(很有可能)完成了一个操作,并且需要调用回调,那么该调用将进入队列。下次代码产生时,将触发队列中的下一个调用。

那么,我们似乎永远不必担心竞态条件。这基本上是对的,但也有微妙之处。例如,考虑以下代码:

var img = document.createElement('img');
img.src = /* ...the URL of the image... */;
img.onload = function() {
    // Handle the fact the image loaded
    foo();
};
doSomethingElse();
doYetAnotherThing();

由于浏览器上的JavaScript是单线程的,我保证在图像加载时获得load事件,对吗?

是错误的。JavaScript代码是单线程的,但环境的其余部分可能不是。因此,在设置了img.src之后,浏览器可能会看到它有一个可以使用的图像的缓存副本,因此它会触发img 上的img.src = ...行和img.onload = ...行之间的load事件。因为我的处理程序还没有被附加,所以我没有得到调用,因为当我附加了我的处理程序时,事件已经触发了。

但是你可以看到排队的效果,如果我们把这些行倒过来:

var img = document.createElement('img');
img.onload = function() {
    // Handle the fact the image loaded
    foo();
};
img.src = /* ...the URL of the image... */;
doSomethingElse();
doYetAnotherThing();

现在我在设置src之前挂钩事件。如果事件在img.src = ...行和doSomethingElse行之间触发(因为浏览器在缓存中有图像),则对处理程序的回调将排队doSomethingElsedoYetAnotherThing在我的处理程序之前运行。只有当控制从我的代码中传递出去时,对处理程序的队列调用才最终运行。JavaScript代码是单线程的,但环境不是。

您还可以以不明显的方式屈服于主机环境。例如,通过调用alert或它的兄弟confirmprompt等。这些函数在现代JavaScript中很显眼,因为它们不是事件驱动的;相反,在显示模态窗口时暂停JavaScript执行。但正如bobince在他的深入讨论中指出的那样,这并不意味着在显示模态时其他代码都不会运行。它仍然是单线程的,但一个线程被挂起在一个地方(通过模态),并用于运行代码在其他地方的同时;这确实是一个非常细微的区别。(Bob还指出了一些事件处理-他的focus示例-似乎打破了这一规则,但它没有。他的示例调用 focus,后者依次调用事件处理程序,然后返回;这和你调用自己的函数没什么不同。)Bob指出的项目的关键共同点是,你的代码调用了主机环境中的一些东西,这些东西会消失并做一些事情(显示一个模态对话框,触发blurfocus处理程序,等等)。

(alert和它的兄弟会引起各种各样的麻烦,特别是在focusblur周围,我建议避免使用它们,以支持更现代的技术(也可以看起来更好18倍)。

这些就是答案开头提到的注意事项。特别是,如果myFunction调用alert,至少在Firefox上ajax完成回调将在alert期间运行(在大多数其他浏览器上不会)。如果你想知道在alerts期间会发生什么,不会发生什么,这里有一个测试setTimeout和ajax的测试页面;你可以进一步扩展测试。

这些答案都是错误的[编辑:当这个答案发布时至少有3-4个错误]。XHR事件放在事件队列/池中。当单线程javascript线程不忙时,它将从事件池中抓取下一个事件,并对它们进行处理。其中一个事件将是您的XHR"AJAX"请求,它将触发回调,然后执行回调。这就是所有没有中断的事件驱动系统的工作方式。

edit: Upvoting Joe的答案链接到JavaScript保证是单线程的吗?并提到了微妙的边缘情况。非常丰富。

Javascript类型的单线程参见Javascript保证是单线程的吗?所以答案是否定的,你的事件处理程序和你的函数可以在同一时间运行,或者他们可能不会。

AJAX回调将在下一个事件循环中运行。

同时调用。因此,当ajax调用成功时,将调用回调函数,从而调用myfunction。

您可以通过添加alert('first one')来快速测试这一点;alert('第二个');在这两个函数中,看看哪个模态弹出先出现。如果您的代码确实受到顺序的影响,请在其中添加一些条件检查以防止/允许某些事情。原生js的速度与往返服务器的速度是完全不同的。除非在无限循环中运行一些javascript,否则在执行其他函数期间返回ajax调用的几率很小。

AJAX调用是"异步的",这意味着"触发一个调用,让我知道你想让我在我完成后回叫什么"。

so简而言之:

function DoSomething() { AJAXCall(Callback); }
 ... some time passes then ...
function Callback() { Done(); }

在这两个函数调用之间任何事情都可能发生,当响应返回时回调仍然会被调用,因为尽管javascript运行时是单线程的,浏览器将以某种顺序堆叠调用(可能是它们的顺序,但不保证)。

很遗憾,我刚刚完成了我的旧网站,因为我有一个经典的例子,一个页面在加载时进行了几个ajax调用,当每个调用返回时,它将填充页面的各个部分。

虽然每个调用都"附加"到文档加载事件,但一次只能进行一个调用,但是服务器可以以任何顺序响应任何调用,因为服务器可以是多线程的(很可能是)。

把javascript想象成一个虚拟的盒子,浏览器可以在其中传递一小块代码,它一次只能运行一个块,但它可以以任何它认为合适的顺序运行大量脚本(通常这是不可预测的多个ajax调用,因为谁知道什么时候服务器可能会返回结果)

当ajax成功调用myCallback时,它将同步运行。所以myCallback和其他函数一样,你先调用它它会先运行或者你最后调用它它会最后运行。他们不会同时跑。