闻心阁

一蓑烟雨看苍生,半壶浊酒笑红尘

浅说JavaScript之Event Loop(上)

2018-03-14 约 1 分钟读完 搬砖秘籍

Event Loop 也叫事件循环,是 JavaScript 中的基础概念,听起来蛮唬人,查了一些资料,斗胆用大白话来说说这个东西。PS:仅限浏览器的讨论。

为什么会有 Event Loop

这一切还要从 JavaScript 的诞生说起,因为造它的用途简单,就是来浏览器“谈谈心”。谈心嘛,套路什么的不用太深,给你一个单线程的脑子就够了。于是乎,这“一根筋”的 JavaScript 就来到了人间,虽然这个呆头呆脑的家伙后来上了一件叫 “Worker” 的装备,也开始学会了城里人的套路,但它总归是村里出来的(web worker本身限制诸多,有空再叨),再多的套路也没变了它“一根筋”的本质。

虽然它“一根筋”,但它不傻。不管工作多么忙碌,人家照样张弛有度。举例来说,就像衣服放洗衣机了,它才不会在那等着45分钟衣服洗完去晒,而是继续扫地、打麻将…等听到洗衣机洗完响了,再去收。它的这个本事就叫“非阻塞”,而让它能有这个本事的,就是今天的主角——Event Loop。

Event Loop 的流程

说起 Event Loop 的流程,要先看这张来自 Philip Roberts 的图。

基本的过程就先从左边看起,JavaScript 主线程运行的过程中产生了 堆(heap) 和 栈(stack),栈中的代码顺序执行,当遇到“拦路虎(异步)”操作的时候,就把这“锅”扔给浏览器老大哥,并对它说“大哥,这事我不好搞,你帮搞定吧。对了,搞定完了,和我弟(任务队列/task queue)说一声啊。”。然后继续栈中的代码跳过这,继续往下走,再遇到(比如DOM操作,AJAX请求)再找老大哥。。。

当栈中的活都干完了,“哎?大哥帮我把事处理的怎样了?找我弟问问去。”

老弟(任务队列)给了一个列表:二哥,这些活(回调),还要你干。

于是,把任务队列的活再接着干,直到没活可干,就可以休息了。

这就是上面图的流程,所以同步总要比异步的结果先出来。比如:

setTimeout(function(){
  console.log('2')
}, 0)
console.log('1')
console.log('3')

结果: 1 3 2

老弟的账本(任务队列的秘密)

既然 JavaScript 这么信任小弟,那小弟做事也不能马虎,对浏览器老大哥也是言听计从。可老弟也是一个有原则的人。当浏览器老大哥给来一堆任务的时候,哪个在前,哪个在后呢?

老弟内心也门清,他把这些任务分为了2类,当官的(micro task)和老百姓(macro task)。任务来了,他就先看看身份,然后把他们放到不同的队伍中去(干部的纯洁性嘛)。于是当大任务(好处)来临时,干部优先嘛,先处理micro task,再处理macro task。

那何人可为官?

new Promise()
new MutaionObserver()

老百姓是谁?

setInterval()
setTimeout()

所以下面的代码:

setTimeout(function () {
    console.log(1);
});

new Promise(function(resolve,reject){
    console.log(2)
    resolve(3)
}).then(function(val){
    console.log(val);
})

结果是: 2 3 1

总结

关于 Event Loop 就先说到这,需要注意的是,Node中的 Event Loop 和浏览器实现还有些差别,有空再叨。