✨♻️ 可视化事件循环
这是一篇我自己翻译的前端基础博文,原作者是Lydia Hallie。 想看原文的小伙伴可以直接转到 ✨♻️ JavaScript Visualized: Event Loop
here we go!
正文开始👉
✨♻️JavaScript 可视化: 事件循环(Event Loop)
事件循环是那种每个JavaScript
开发人员都必须以某种方式应对的事物之一,但一开始可能有点难以理解。作为一个视觉学习者,我想通过以低分辨率的gif
形式向你解释它,因为现在是2019年,gif
还是有些像素化和模糊。
但首先,什么是事件循环,为什么你应该关注它?
JavaScript
是单线程的:一次只能运行一个任务。通常这没什么大不了的,但想象一下你正在运行一个需要30秒的任务...是的...在这个任务期间,我们需要等待30秒才能进行其他任何操作(JavaScript
默认在浏览器的主线程上运行,因此整个UI都会被挂起)😬 这是2019年了,没有人想要一个慢速且无响应的网站。
幸运的是,浏览器给了我们一些JavaScript
引擎自身没有提供的功能:Web API
。这包括DOM
API
、setTimeout
、HTTP
请求等等。这可以帮助我们创建一些异步、非阻塞的行为🚀
当我们调用一个函数时,它会被添加到一个叫做调用栈的地方。调用栈是JS引擎的一部分,这不是与特定浏览器相关的概念。它是一个栈,意味着它是先进后出的(想象一堆煎饼)。当一个函数返回一个值时,它会从栈中弹出👋
respond
函数返回一个 setTimeout
函数。setTimeout
由Web API提供,它允许我们延迟任务而不会阻塞主线程。我们传递给 setTimeout
函数的回调函数,箭头函数 () => { 返回 'Hey' }
被添加到Web API中。同时,setTimeout
函数和 respond
函数从栈中弹出,它们都已经返回了它们的值!
在Web API中,一个定时器会运行我们传递给它的第二个参数指定的时间长度,比如1000
毫秒。回调函数不会立即被添加到调用栈,而是被传递给一个称为"队列"的东西。
这可能是一个令人困惑的部分:这并不意味着在1000
毫秒后回调函数被添加到调用栈(从而返回一个值)!它只是在1000
毫秒后被添加到队列中。但是队列是一个队列,函数必须等待它的轮到!
现在,这是我们一直在等待的部分... 事件循环该做它唯一的任务了:连接队列和调用栈!如果调用栈为空,也就是说之前调用的所有函数都已经返回了值并从调用栈中弹出,那么队列中的第一个项会被添加到调用栈中。在这种情况下,没有其他函数被调用,这意味着在回调函数成为队列中的第一个项时,调用栈为空。
回调函数被添加到调用栈中,被调用并返回一个值,然后从栈中弹出。
阅读文章是有趣的,但只有通过不断地实际应用和实践,才能真正对此感到舒适。尝试弄清楚如果我们运行以下代码会打印什么到控制台:
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 API中,setTimeout
函数和bar
函数从调用栈中弹出。 - 定时器运行,在此期间调用了
foo
函数并打印了"First"。foo
函数返回(undefined
),随后baz
函数被调用,回调函数被添加到队列中。 baz
函数打印了"Third"。事件循环在baz
函数返回后发现调用栈为空,随后将回调函数添加到调用栈中。- 回调函数打印了"Second"。
希望这篇文章能让你对事件循环更加舒适!如果仍然感到困惑,不用担心,最重要的是理解某些错误/行为可能出现的原因,以便有效地搜索正确的术语,并找到正确的 Stack Overflow
页面 💪🏼 如果有任何问题,请随时向我提问!
😊okk~ 翻译完了,纯人工翻译,希望能够帮到各位,如有纰漏,欢迎指正~