事件侦听器调用函数后,代码的执行在哪里继续

Where does execution of code continue after a function is called by an event listener?

本文关键字:执行 在哪里 继续 代码 侦听器 调用 函数 事件      更新时间:2023-09-26

编辑:对于任何困惑,我深表歉意。在我的代码中,循环在脚本中无限期运行,直到达到某个条件。我的问题是,如果事件侦听器在此循环运行时调用函数,则在函数完成后,执行将在哪里继续?

嗯,我想这几乎是不言自明的。事件侦听器调用函数后,函数完成后代码的执行在哪里继续?

有一个事件队列,该队列中的下一个都会被执行。例如,它可以是鼠标单击事件、窗口大小调整或超时。如果队列上没有任何内容,则运行 JavaScript 的容器将循环,直到队列中有要处理的内容。

您可以在 MDN 关于"并发模型和事件循环"的文章中阅读更多内容:

JavaScript 运行时包含一个消息队列,它是要处理的消息列表。与每条消息相关联一个函数。当堆栈为空时,消息将从队列中取出并进行处理。处理包括调用关联的函数(从而创建初始堆栈帧)。当堆栈再次变为空时,消息处理结束。

将某些内容放入消息队列的常用方法是调用

setTimeout(myfunction, 0);

由于第二个参数的延迟为 0,一旦当前执行的代码完成,函数 myfunction 将被调用,即调用堆栈变为空。但请注意,队列中可能已经存在其他一些事件。在这种情况下,这些仍将首先执行。

什么长循环?

您在问题中添加了以下内容:

在我的代码中,循环在脚本中无限期运行,直到达到某个条件。我的问题是,如果事件侦听器在此循环运行时调用函数,则在函数完成后,执行将在哪里继续?

如果您的循环不通过异步调用运行(例如,重复调用setTimeoutsetInterval),而是如下所示:

while (!endingCondition) {
    // do nothing
}

那么就不可能调用另一个(JavaScript)事件侦听器。此类事件将在消息队列上等待。仅当当前循环及其完成后的任何代码时,才会处理该事件并导致事件侦听器的调用。

因此,让我们看以下示例:

var clicked = false;
document.onclick = function() {
    clicked = true;
}
while (!clicked) {};
alert('clicked!');

在这里,人们可能希望while循环被单击中断并显示警报,但事实并非如此。click 事件将位于操作系统的消息队列中,但不会被 JavaScript 使用,因为它总是首先完成当前正在执行的代码。正如上面关于MDN的文章所述:

每当函数运行时,它都不能被抢占,并且将在任何其他代码运行之前完全运行。

任何代码都包含所有 JavaScript 代码,包括事件处理程序中的代码。

如何编写"可中断"代码

上面的例子可以像这样工作:

var clicked = false;
document.onclick = function() {
    clicked = true;
}
function detectClick() {
    if (!clicked) {
        setTimeout(detectClick, 0); // keep repeating
        return;
    }
    alert('clicked!');
    // do something more ...
};
detectClick(); // call for first time
// We get here immediately. Code will end, and events will be processed.
// One of those "events" is a time-out, to which we have set a handler: detectClick.
// At some point a click event will be there as well, triggering the other handler.