myfreax

JavaScript 事件轮询

在本教程中,您将了解 JavaScript 中的事件轮询以及 JavaScript 如何基于事件轮询实现并发模型

JavaScript 事件轮询
JavaScript 事件轮询

在本教程中,您将了解 JavaScript 中的事件轮询以及 JavaScript 如何基于事件轮询实现并发模型。

JavaScript 单线程模型

JavaScript 是一种单线程编程语言。这意味着 JavaScript 在一个时间点只能做一件事。

JavaScript 引擎从文件顶部并向下执行开始执行脚本。它在执行阶段创建执行上下文,将函数压入和弹出调用栈

如果一个函数需要很长时间才能完成执行,那么在函数执行期间您将无法与 Web 浏览器交互,因为页面会挂起。

需要很长时间才能完成的函数称为阻塞函数。从技术上讲,阻止函数会阻止网页上的所有交互,例如鼠标单击。

阻塞函数的一个示例是从远程服务器调用 API 的函数。下面的例子使用一个大循环来模拟阻塞函数:

function task(message) {
    // 模拟消耗大量时间的任务
    let n = 10000000000;
    while (n > 0){
        n--;
    }
    console.log(message);
}

console.log('Start script...');
task('Call an API');
console.log('Done!');

在这个例子中,我们在 task() 函数中有一个 while 循环来模拟一个耗时的任务。因此 task() 函数是一个阻塞函数。

脚本挂起几秒钟并打印以下输出到控制台:

Start script...
Download a file.
Done!

为了执行脚本,JavaScript 引擎将第一个  console.log() 调用放在调用栈的顶部并执行并在完成后弹出  console.log()。然后,它将 task() 函数放在调用堆栈的顶部并执行 task() 函数。

但是,完成 task() 函数需要一段时间。因此,稍后您会看到消息 'Download a file.'task() 函数完成后,JavaScript 引擎将其从调用栈弹出 task() 函数。

最后,JavaScript 引擎将最后一次调用 console.log('Done!') 函数并执行它,这将非常快。

事件轮询与回调

为了防止阻塞函数阻塞其他活动,您通常将其放在回调函数以便稍后执行。例如:

console.log('Start script...');

setTimeout(() => {
    task('Download a file.');
}, 1000);

console.log('Done!');

在此示例中,您将立即看到 'Start script...''Done!' 消息 。之后,您会看到消息'Download a file'

这是输出:

Start script...
Done!
Download a file.

如前所述,JavaScript 引擎一次只能做一件事。但是,更准确地说,JavaScript 运行时一次只能做一件事。

Web 浏览器还有其他组件,而不仅仅是 JavaScript 引擎。当您调用 setTimeout() 函数、发出 Fetch 请求或单​​击按钮时,Web 浏览器可以并发和异步地执行这些活动。

Fetch 请求 ,setTimeout()DOM 事件是浏览器 Web API 的一部分。

在我们的示例中,当调用 setTimeout() 函数时,JavaScript 引擎将其放在调用栈中,Web API 创建一个 1 秒后到期的计时器。

然后 JavaScript 引擎将 task() 函数放入称为回调队列或任务队列的队列中:

事件轮询是一个不断运行的进程,它同时监听回调队列和调用栈。如果调用栈不为空,则事件轮询等待直到它为空,然后将回调队列中的下一个函数放入调用栈。如果回调队列为空,则什么也不会发生:

看另一个例子:

console.log('Hi!');

setTimeout(() => {
    console.log('Execute immediately.');
}, 0);

console.log('Bye!');

在此示例中,超时为 0 秒,因此消息 'Execute immediately.' 应出现在消息 'Bye!' 之前 。但是,它不是那样工作的。

JavaScript 引擎将以下函数调用放在回调队列中,并在调用栈为空时执行它。换句话说,JavaScript 引擎在执行 console.log('Bye!') 之后再运行 console.log('Execute immediately.');  .

console.log('Execute immediately.');

这是输出:

Hi!
Bye!
Execute immediately.

下图说明 JavaScript 运行时、Web API、调用堆栈和事件轮询:

结论

在本教程中,您了解了 JavaScript 事件轮询,这是一个不断运行的进程,它协调调用栈和回调队列之间的任务并实现并发。

内容导航