前言
今天该学习 Event Loop 啦,其实之前我写过一篇 Event Loop 的文章:
浅析 JS 中的 EventLoop 事件循环(新手向)
这篇呢则是动图学 JS 系列中的,可以结合之前的文章食用~
我们都知道 JavaScript 是一门 单线程 的语言:同一时间只能运行一个任务。通常情况下这没什么问题,但是如果你有一个任务需要耗费 30 秒的时间,那其他任务难道都要等它 30 秒么?(由于 JS 运行在浏览器的主线程,所以这 30 秒的时间里,整个页面都会处于卡死状态)
幸运的是,浏览器提供了一些 JS 引擎不具备的功能:Web API。它包括 DOM API
,setTimeout
,HTTP 请求
等等。这些功能都可以帮助我们处理 异步、非阻塞 的操作。
调用栈
当我们调用一个函数时,它会被添加到一个叫做 调用栈 (call stack) 的地方,调用栈是 JS 引擎的一部分,而不是浏览器特有的。本质上它是一个栈,具有 后进先出 (Last In, First Out. 即 LIFO) 的特点。当一个函数调用完成,它就被从调用栈中弹出。
上图中函数 respond
返回了一个 setTimeout
函数,它也被添加到调用栈中,(setTimeout
正是 Web API 提供的功能之一:它可以让我们延迟一个任务的执行并且不阻塞主线程。)setTimeout
被调用之后,传给它的箭头函数 () => { return 'Hey' }
就被添加进了 Web API (此处简化了概念,具体可以看笔者的另一篇文章)中。同时 setTimeout
和 respond
函数从调用栈中弹出,它们都返回了相应的值。
任务队列
在 Web API 中,一个定时器已经创建,它将会等待 1000 ms,当时间到后,这个箭头函数并不会立即被调用栈执行,它会被添加到一个队列中,我们暂且称之为 任务队列 (原文中叫 Callback Queue)。
这里可能会让人困惑:那个回调箭头函数并不是在 1000ms 后被直接添加到 调用栈 的,而是被添加进了 任务队列。队列嘛,就是大家排队,先来的先服务,被谁服务?没错!就是调用栈。
事件循环
说了这么多,终于轮到我们的 Event Loop 登场了!如果上面的调用栈是一个银行窗口,任务队列中的回调函数是一个个排队办业务的人,那么 Event Loop 就是叫号系统!Event Loop 的唯一任务就是 连接任务队列和调用栈:
它不停检查 调用栈 中是否有任务需要执行,如果没有,就检查 任务队列,从中弹出一个任务,放入调用栈中,如此往复循环。
上图中终于轮到那个箭头函数接受调用了,它被调用完,也被弹出了,轻轻地它走了,只留下一个 Hey!
o(╯□╰)o
一个例子
看图片是不是挺好理解的~ 那就来看一个例子,可以把下面的代码粘贴到浏览器的控制台亲自跑一下:
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
被调用,打印出Third
。 - 500ms 定时器结束,回调函数被放入任务队列,Event Loop 检查到调用栈是空的,所以将其取出放在调用栈。
- 回调函数被执行,打印出
Second
。
全文就到这里啦,希望对你理解 Event Loop 有所帮助~
本文是翻译的系列文章:
- 动图学 JavaScript 之:声明提升(Hoisting)
- 动图学 JavaScript 之:作用域链(Scope Chain)
- 动图学 JS 之:事件循环(Event Loop)【本篇】
- 动图学 JS 之:JavaScript 引擎
- 动图学 JS 之:原型继承
本文首发于公众号:码力全开(codingonfire)
本文随意转载哈,注明原文链接即可,公号文章转载联系我开白名单就好~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。