Eventloop事件循环,到底是个啥?
本文介绍所谓EventLoop相关内容
在具体讲这个话题前,我强烈推荐大家去看一篇文章,就是朴灵对阮一峰讲解的eventloop文章的注解,很有意思。对于操作系统和硬件层面的内容有过学习的同学,看完之后的感觉相信跟我是一样的,高下立判。类比有时候是需要的,但类比终究是类比,他会丧失很多细节。最严重的,类比到了最后,就变成了胡扯。
专业点讲,js是一门非阻塞的单进程单线程语言,就是说,不存在并行代码,只能一行一行的执行。应用场景决定了语言特性。js起家就是用于操作dom。倘若js是多线程。目下有两个线程,其中一个改了节点内容,另一个删除了节点,执行哪个?注意:线程的执行是并行的。所以,可以姑且认为现在是单线程,今后至少在相当长的时间内,依旧会是单线程。
那么一个显然的问题就来了,我们经常听到的js中的异步请求是什么鬼?不是说是单线程吗?异步任务是谁在执行?是跑js的那个环境,需要严重分清一个概念,循环队列这种规则机制,不是js的运行机制,而是跑js的那个运行环境的运行机制。对于Node来说是V8、对Chrome来说是V8、对Safari来说JavaScript Core,对Firefox来说是SpiderMonkey。
例如为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,在具体的业务需求中,我们通常将一些大数据量的计算操作,塞给一个worker去单独处理,以此避免阻塞主执行线程。
同步就是调用之后一直等待,直到返回结果。异步则是调用之后,不能立刻拿到结果,通过一系列的手段才最终拿到结果(所以调用之后,拿到结果中间的时间可以介入其他任务,等有结果返回,执行回调函数即可)。
一张图解释二者执行流程:
注意:这里的事件队列只是一个统称。不同的事件,被放置在不同的事件队列中。各自的优先级有所不同,实质是各自的watcher先后顺序的不同而已。
这些不同优先级的事件,大体可以分为两种:就是宏任务事件和微任务事件。我们只需要知道一点,微任务优先级高于宏任务。如下图所示:
从图中能够看出,js执行完自身任务栈中的任务后,首先会遍历微任务队列,执行完所有微任务后,再去遍历宏任务队列,同时,每执行完一个宏任务都会再去查一下有没有微任务。总而言之,微任务优先。
通过上面的内容介绍,这个问题的答案,已经很明显了。主线程一旦空闲就会不停的问候事件队列,查看是否存在任务在等待。类比就是个轮回循环。
补一个经典的微任务:dom监听器MutationObserver的使用示例
const textEl = document.getElementById('textEl');
// 监听文本变化
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
console.log('文本从「', mutation.oldValue, '」变成「', textEl.textContent, '」');
});
});
// 配置:监听文本变化 + 记录旧值
observer.observe(textEl, {
characterData: true, // 监听文本变化
characterDataOldValue: true // 记录变化前的旧文本
});
