Js 执行机制/eventloop
原创文章,未经允许,请勿转载
基本概念
函数调用栈)) queue((queue
事件队列)) heap((heap
对象分配区))
function c() {
a()
b()
}
function m() {
c()
}
m()
函数调用栈的过程(从栈底往栈顶看)
b b入栈,执行,执行完后出栈,栈变短
a a入栈,执行,往下执行,发现b
c c执行,发现a
m 入栈,执行 发现调用c,于是c入栈
event loop 简化模型
js是单线程的,事件驱动的,这里的事件包含:I/O、网络、计时器、鼠标、键盘输入等等
while (queue.waitForEvent()) { //等待事件,如有用户点击按钮或者定时器到期
queue.processNextEvent();//执行事件,这里可能会产出新事件,如:调用了 setTimeout,新事件会送入到队列中,等待被后面的循环执行
}
为什么单线程?我们假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加了一个节点,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准呢?基于此,其他所有操作系统所有框架,UI线程都只有一个
现在我们允许使用 Web Worker 创建一个子线程,但是worker不能直接操作UI,必须把数据提交给主线程,通过主线程操作UI
基于 event loop 的逻辑,我们考虑到 setTimeout(callback,0)
里的 callback
并不会立即执行,它首先依赖于队列的长度,最早也是下一班车发车时被执行
所以 setTimeout
并不会100%的精确度按照我们希望的时间去执行,而是取决于当前事件队列的长度,如果事件很多,那么就可能被延迟执行
下面的代码演示了这一点
const s = Date.now();
setTimeout(function() {
//这里并不会在500毫秒后立即打印下面这行log
console.log("经过了 " + (Date.now() - s) + " 毫秒");
}, 500);
//这里阻塞了2秒,因为是单线程,所以这2秒内,其他事件都没机会执行
while(true) {
if(Date.now() - s >= 2000) {
console.log("2秒过去了");
break;
}
}
深入理解js的执行机制
js的事件队列细分为两种,microtasks
和 macrotask
,两种队列在event loop中的处理方式不一样
考虑下面代码的执行结果是怎样的?
//立即执行
console.log(1)
//把callback放到 task queue
setTimeout(()=>{
console.log(2)
},0)
function asyncFunc(){
return new Promise((resolve)=>{
resolve()
})
}
//把callback放到 task queue
asyncFunc().then(()=>{
console.log(3)
})
//立即执行
console.log(4)
//把callback放到 task queue
setTimeout(()=>{
console.log(5)
},0)
结果是:
1 ----
4 | 一班车
3 ----
2 二班车
5 三班车
描述 microtasks
和 macrotask
的简化模型如下
while (eventLoop.waitForTask()) {
//macroTask会在每一班车的最先执行,每班车只处理一个task
const taskQueue = eventLoop.selectTaskQueue()
if (taskQueue.hasNextTask()) {
taskQueue.processNextTask()
}
//microTask会在当班车的末尾集中处理
const microtaskQueue = eventLoop.microTaskQueue
while (microtaskQueue.hasNextMicrotask()) {
microtaskQueue.processNextMicrotask()
}
}
以下API的回调会推送到 macroTask 宏任务队列:setTimeout
setInterval
setImmediate
以下API的回调会推送到 microTask 微任务队列:process.nextTick
Promise
MutationObserver
浏览器js执行是不会阻塞的,通过事件队列,让渲染、网络、IO等操作能在单线程的事件循环中被分开执行,充分发挥CPU的效率,保证界面交互的流畅度,但是
alert
和sync的XHR
是阻塞的,调用alert
之后,js阻塞,直到alert
框关闭,才执行后面的语句
知识延伸
不只是浏览器的工作原理是基于事件驱动,实际上大部分操作系统的应用逻辑都类似
windows 的 WindowProc
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
mac OS 的 [NSApp run]
void PollEvents() {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc]init];
for(;;) {
NSEvent* event = [NSApp nextEventMatchingMask : NSEventMaskAny
untilDate : [NSDatedistantPast] inMode : NSDefaultRunLoopMode
dequeue:YES];
if(event == nil)
break;
[NSApp sendEvent : event];
}
[pool release];
}
while(true) {
PollEvents();
}
来源:悠游悠游,2019-05-31,原文地址:https://yymmss.com/p/event-loop.html