JS图解Event Loop

202 阅读3分钟

Javascript是单线程的,但是,开发人员可以用Javascript编写异步代码!

怎么可能?事件循环让它成为可能。

在开始之前,让我们定义事件循环运行的环境。我们假设我们正在处理浏览器(不在Node或其他环境中)执行的Javascript代码。

首先我们来认识下故事中的主角:

调用栈 (the call stack)

调用堆栈是内存中的一个位置,用于跟踪当时执行的功能以及此后将要执行的功能。每个功能都位于上一个功能的顶部。添加的第一个函数将最后执行(先进先出)。

Web API

Web API并不是核心JS的一部分,而是提供了可以由Javascript程序使用的各种方法,如:setTimeout()或alert()。

消息队列 (message queue)

消息队列是等待其关联函数执行的消息列表。每当事件侦听器监视事件发生时,列表中将添加一条新消息。

事件循环 (the event loop)

事件循环是一个持续运行并检查调用堆栈是否为空的进程,如果调用堆栈为空,它会将消息队列的第一项推入调用栈进行执行。

这是浏览器环境

接下来开始JS故事 让我们看看下面的代码,看看会发生什么

调用foo函数,结果是:

> One
> Three
> Two

现在,让我们看看我们的主角试图执行上述代码:

首先,浏览器发生foo()调用堆栈的函数调用;

console.log("One")语句被推到前一帧的顶部;

同时,事件循环检查调用堆栈是否为空;

Js 运行时执行顶部帧并将其从调用堆栈中删除;

继续执行,浏览器将setTimeout()语句发送到堆栈;

事件循环再次检查;

该环境设置了一个计时器,该计时器将触发内部的回调setTimeout

下一个语句被推入调用堆栈;

事件循环再次检查;

事件循环检查发现堆栈不是空的,所有这里不做任何操作。 执行过程继续下一个foo ()函数的语句,也是就是这里的最后一个;

让我们回到 web API,它为回调函数设置了一个计时器。现在计时器已经结束,浏览器将回调消息发送到消息队列

在执行最后一个语句之后,它将从堆栈中删除,并且,当foo()函数声明中没有其他内容时,最早的foo()帧也将从调用堆栈中删除!

现在,事件循环可能更幸运

事件循环检查队列中等待的任何消息;

并将消息的相关函数发送到调用堆栈;

最后,JS 运行时执行最后一帧并从调用堆栈中删除它

总结: 语言本身没有任何异步内置。环境(浏览器等)是安排导致异步执行的事件的环境(使用回调,promises、ajax 调用等)

原文地址