背景
JavaScript 是单线程运行的,但是一定会有异步请求(如Ajax,定时器等),因此异步就要基于回调来实现
Event Loop 就是异步回调的实现原理
单线程的JavaScript
那么 JavaScript 是如何执行的呢?
- 从前到后,一行一行执行
- 如果执行到某一行报错,则停止下面代码的执行
- 会先把同步代码执行完,再执行异步
因此,基于上述给出一个基本的异步回调的实现原理
执行过程
给出执行代码
console.log('hi');
setTimeout(function cb1() {
console.log('cb1'); //cb 即callback
}, 5000);
console.log('Bye');
其中:
Browser console
是控制台输出
Call Stack
是真正用来触发某行代码的
Web APIs
用来处理外部定时等异步的API的
Event Loop
是当Call Stack
为空的时候,即同步代码停止时就会触发,不停的从Callback Queue
中查找有没有能执行的函数,如果有的话就把它从Callback Queue
移动到Call Stack
中执行
开始
Step1
执行第一行代码
将同步代码放入 Call Stack
中
Step2
执行 Call Stack
中的内容并移除
在Browser console
输出 Hi,然后移除第一行代码,JS 引擎继续向下执行
Step3
执行第二行代码
将同步代码 setTimeout 放入Call Stack
中,其中有个函数名叫 cb1
Step4
执行异步函数里的内容
因为 setTimeout 为异步函数,所以将函数中的内容放入Web API
中,让他等待被执行,因为设定的等待时间为5秒钟,但是对于 JS 引擎而言,5秒钟是一个漫长的时间。这样 setTimeout 的使命就结束了。
Step5
移除 Call Stack
中的内容
Step6
执行第五行代码
将同步代码放入Call Stack
中
Step7
执行Call Stack
中的内容并移除
在 Browser console
输出 Bye,然后移除第五行代码。
Step8
Call Stack
为空并且无后续代码,这时执行 Event Loop
就从Callback Queue
中去循环查找可循环的内容来执行,但发现此时没有
Step9
等待5秒之后,cb1 会进入 Callback Queue
中
这时 Event Loop 就生效了,将 cb1 放入 Call Stack
中
Step10
执行 Call Stack
中的内容
然后执行函数体的内容
打印 cb1 ,并从 Call Stack
中移除
这样,历经10+个步骤,程序就结束了
总结
-
同步代码,一行一行的放在Call Stack中执行
-
遇到异步,会先“记录”下,等待时机(定时,网络请求等)
-
时机到了,就移动到 Callback Queue里
-
若Call Stack 为空(即同步代码执行完) Event Loop开始工作
-
轮询查找Callback Queue,如有则移动到Call Stack中执行
-
然后继续轮询查找(像永动机一样)
进一步探讨
DOM 事件和 event loop 的关系
因为 JS 是单线程的,而且和 DOM 渲染共用一个线程。所以 JS 执行的时候,得留一些时机供 DOM 渲染。
event loop 其实还有一步,在Call Stack
中,若同步任务执行完了,其实是会先去尝试 DOM渲染,然后再去触发 event loop,然后去 Callback Queue 中取内容。
微任务和 event loop 的关系
微任务:DOM渲染前触发,如Promise,async/await
因此最终的过程