原文地址:https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif
哦,事件循环,是每个前端开发者都必须以某种方式处理的事情之一,但是一开始理解起来可能会感到疑惑。我是一个视觉学习者,所以我想通过低分辨率的 GIF 图片以视觉方式解释来尝试帮助你,因为现在都2019年,GIF 图片不知为何还是像素化和模糊的。
但是开始,应该知道什么是 Event-Loop 以及为何你应该关注它?
JavaScript 是单线程:同一时刻只能执行一个任务。通常情况下没什么大问题,但是现在假设你在执行一个耗时 30s 的任务。啊,在那个任务期间,我们需要等待 30s 才能执行其他任务(默认情况下,JavaScript 执行在浏览器的主线程上,所以整个 UI 会被卡住 )。😭现在都 2019 年了,没有人希望慢且没有响应的网站。
幸运的是,浏览器给了我们一些 JavaScript 引擎自身不提供的特性:Web 接口。这些包括 DOM 接口、setTimeout
、HTTP 请求等等。这些特性可以帮助我们创建一些异步、非阻塞行为 🚀。
当我们调用一个方法时,它会被加入到一个叫做调用栈的里面。调用栈是 JavaScript 引擎的一部分,这与浏览器无关。它是一个栈意味着是先进后出(想起了一堆煎饼)。当一个函数返回值后,它就会被从栈顶抛出。
respond
函数返回一个 setTimeout
函数,setTimeout
是 Web 接口提供给我们的:它使我们能在不阻塞主线程的情况下延迟一些任务。我们传递给 setTimeout
的箭头函数 ()=>{return 'Hey'}
被加载到浏览器提供的 Web 接口中。同时,setTimeout
和 respond
函数从栈中弹出,它们都返回了各自的值。
在 Web 接口中,计时器的将要运行的时间与我们传递给它的第二个参数一样长,即 1000ms。回调并不是直接添加到调用栈中而是被加到一个叫做队列的东西中。
这可能是令人迷惑的部分:1000ms 后它不是被添加到调用栈中,而是被添加到队列中。由于这是一个队列,这个函数必须等待轮到它时才能执行。
现在我们都在等待 Event loop 执行它的任务所需时间:连接队列与调用栈!若调用栈空了,意味着之前所有调用的函数已经返回它们各自的值而且从栈中被抛出,同时队列中的第一项会被加到调用栈中。在这种情况下,没有其它的函数调用,意味着当回调函数称为队列中的第一项时调用栈是空的。
回调函数被添加到调用栈中,执行以及返回值,最后从栈中抛出。
读文章是很有趣,但你只有通过反复实际操作才能完全适应。试着想一下,如果我们运行下面的程序,控制台中会打印什么记录。
const foo = () => console.log("First");
const bar = () => setTimeout(() => console.log("Second"), 500);
const baz = () => console.log("Third");
bar();
foo();
baz();
明白了吗?让我们快速看下当在浏览器中运行上面的代码发生了什么:
- 我们调用
bar
函数,bar
返回一个setTimeout
函数。 - 我们传递给
setTimeout
的函数被添加到浏览器提供的 Web 接口中,setTimeout
和bar
函数从栈中抛出。 - 计时器运行的同时,调用
foo
函数并打印出First
,并返回 undefined ,调用baz
函数。以及回调函数添加到队列中。 baz
打印Third
,在baz
返回后 Event loop 的调用栈是空的,接着回调函数添加到调用栈中。- 回调函数打印出
Second
。
希望这能让你对 Event loop 更适应一些。不要担心,如果它仍然令人困惑,最重要的是了解某些错误/行为来自哪里,以便有效的搜索正确术语,并最终进入正确的 StackOverflow 页面。