Event Loop
2022/12/11
听完大佬几年前的JSConf上的分享,感觉收益很多。
首先,JS是一个单线程的语言。这也就意味着,它没有真正的像C/C++那样的Thread,而是只能一次做一件事情。
对于Call Stack,这个对各种语言都差不多,调用栈都有入栈、出栈的操作。但是如果遇到Blocking的事件,比如Alert弹窗,比如Xhr request,比如超级大的数组遍历循环,都会占用Call Stack,导致后续的事件无法进行。
这里还有个概念,就是Chrome等浏览器默认都是16ms 刷新一次页面,每秒60帧。如果js的Call Stack里单个任务调用链的调用时长(即清空Call Stack所需花费的时间)超过16ms,那么必然会block住浏览器请求绘制页面的任务。
对于Node来说,它的backend是C++,对JS代码只开放Web API。这套Web API是由一些支持多线程的语言编写的,比如Node的timer:
node/timers.cc at main · nodejs/node (github.com)
然后其对JavaScript暴露SetTimeout接口。
也就是说,JavaScript里的SetTimeout无非是一个宏定义,其真正做的事情,就是把任务丢给Web API的实现者去做(无论是Node 还是Chrome V8),然后后端引擎做完任务后再把回调任务放到Callback Queue里。
JS的Runtime会在清空Call Stack后,去不断检查Callback Queue,然后把Callback Queue里的任务挪到Callback Stack里执行。
这也是React等框架分块渲染的逻辑:尽量把任务分割得足够小,让浏览器能够频繁地清空Callback Stack,这样每16ms执行的渲染任务才能正常地被执行。