跟着大佬走——浏览器中的eventloop
时间又过去了一周,第一篇文章仿佛就在两周前。如果忘记磐冲的 jd 描述,别忘了回顾下第一篇内容哦:
1、
2、
这一周我们来详细说说浏览器中的 eventloop。
明确:JavaScript 是一个单线程脚本语言。就是说,有一行代码在执行过程中时,必然不会存在同时执行另一行代码的现象。
定义
“Eventloop 定义
w3c 对 eventloop 的定义为:为了协调事件,用户交互,脚本,渲染,网络等,用户代理必须使用事件循环。例如:当浏览器发起一个网络请求时,主程序接收到异步信号后就先去做其他事,当异步完成后,主程序空闲时候则来处理异步后的内容。
“事件定义
w3c 对事件的定义为:由于某种外在或内在信息状态发生变化,从而导致对应反应。例如:用户点击一个按钮。一个事件会包含多个任务。
“事件循环机制
w3c 对事件循环的定义:一个事件循环有一个或者多个任务队列。任务队列为 task 的有序列表。
每一个任务都来自特定任务源,同一任务源且属于特定事件循环的任务,通常被加入一个任务队列,但不同任务源的任务可能会被放入不同任务队列。
每一个事件循环都有一个进入 microtask
检查点的 flag 标志,这个标志初始值为 false,被组织反复调用进入 microtask
检查点的算法。
实现
根据 JavaScript 解释工具不同,js 可运行在浏览器和node中。浏览器中 eventloop 具体实现由不同浏览器厂商完成,node 则使用 libuv 库实现。
这就是说,浏览器的 eventloop 与 node 下的eventloop 是两个概念。
一个事件循环有很多个任务队列源自不同任务源,每一个队列严格按照先进先出顺序执行,但是不同任务队列执行顺序不同,浏览器按照各家标准调度不同任务队列。
“浏览器上的实现
在 js 中,任务被分为两种:宏任务MacroTask
, 微任务MicroTask
。
其中,宏任务包括:
-
script 代码 -
setTimeout -
setInterval -
setImmediate(仅在 IE10 下) -
I/O -
UI Rendering
微任务包括:
-
Process.nextTick(node 独有) -
Promise -
Object.observer(废弃) -
MutationObserver
浏览器实现顺序
在同一上下文中,总得执行顺序为:同步代码 -> 微任务 -> 宏任务。
浏览器会不断从任务队列中按顺序取得 task 执行,每执行完一个任务便查看 microtask 队列是否为空,如果不为空则一次性执行完所有 microtask,再去 task 队列中取下一个 task 执行。
举个例子:
console.log('script start');
setTimeout(function () {
console.log('setTimeout');
}, 0);
Promise.resolve()
.then(function () {
console.log('promise1');
})
.then(function () {
console.log('promise2');
});
console.log('script end');
答案是什么你先想一想
。
。
。
。
。
。
script start
script end
promise1
promise2
setTimeout
怎么样,跟你想的是否一样呢?解释一下运行顺序:
step1:一开始 task 队列中只有 script,将 script 所有函数放入 执行栈。遇到setTimeout
时,将其放入 task 队列中,在下一个事件循环中执行。
step2:遇到promise
后,将其放入 microtask 队列中。
step3:当 script 执行完毕后,检查 microtask 队列,发现不为空后执行 .then
,由于第一个 then 返回依旧是 promise,所以第二个 then 放入 microtask 队列继续执行。
step4:此时,microtask 队列为空了,则进入下一个事件循环,task 队列发现了 setTimeout 后立即执行。
一个思考题
new MutationObserver(
function() {
console.log('mutate');
}).observe(outer, {
attributes: true
});
function onClick() {
console.log('click');
setTimeout(function() {
console.log('timeout');
}, 0);
Promise.resolve().then(
function() {
console.log('promise');
});
outer.setAttribute('data-random', Math.random());
}
inner.addEventListener(
'click',
onClick
);
outer.addEventListener(
'click',
onClick
);
思考一下这个问题的执行顺序是什么?后台浏览与我们一起讨论吧!
参考:
-
https://segmentfault.com/a/1190000013861128 -
https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/ -
https://segmentfault.com/a/1190000010622146