事件循环
- 0
⚠️本篇只讨论浏览器部分
进程(process):计算机已经运行的程序,是操作系统管理程序的一种方式。 线程(thread):操作系统能够运行运算调度的最小单位,通常情况下它被包含在进程中。
浏览器是多进程的,通常每个tab页都是一个进程,一个页面崩溃不会影响其他页面
以谷歌浏览器为例,由以下线程组成
GUI 渲染线程
JavaScript引擎线程
定时触发器线程
事件触发线程
异步http请求线程
GUI渲染线程与JavaScript引擎互斥、所以JS会阻塞页面的加载
管理执行上下文的栈为调用栈、先进后出,当入栈的执行上下文超过了一定数目,JS引擎就会报栈溢出错误
JavaScript执行分为同步任务和异步任务,同步任务压入执行栈中执行,异步任务放入任务队列中,任务队列又分为微任务队列和宏任务队列
setTimeout
,setInterval
,setImmediate
, I/O
, UI rendering
,requestAnimationFrame
Promise
MutationObserver
process.nextTick
, Promises
, Object.observe
, MutationObserver
,fetch
先进先出的数据结构
简单概括
⚠️在所有任务开始的时候,由于宏任务中包括了script,所以浏览器会先执行一个宏任务,在这个过程中你看到的延迟任务(例如setTimeout
)将被放到下一轮宏任务中来执行。
浏览器渲染事件循环
JavaScript是单线程的语言
事件循环是JavaScript的执行机制
JavaScript事件分同步和异步事件
JavaScript引擎在等待任务,执行任务中不断循环,宏任务和微任务执行完成后都会判断是否还有微任务,有的话执行微任务,没有就执行宏任务,如此循环
用代码表示即
for (const macroTask of macroTaskQueue) {
handleMacroTask();
for (const microTask of microTaskQueue) {
handleMicroTask(microTask);
}
}
js
js
Node
的事件循环是基于libuv
实现的
待更新
如果 JavaScript 引擎线程不是单线程的,那么可以同时执行多段 JavaScript,如果这多段 JavaScript 都修改 DOM,那么就会出现 DOM 冲突。所以避免 DOM 渲染的冲突
引入单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这同时又导致了一个问题:如果前一个任务耗时很长,后一个任务就不得不一直等着。
console.log(0)
setTimeout(_ => console.log(4),0)
new Promise(resolve => {
resolve()
console.log(1)
}).then(_ => {
console.log(3)
})
console.log(2)
async function fn1() {
console.log(1)
}
async function fn2() {
console.log(2)
}
async function result() {
await fn1()
fn2()
setTimeout(() => {
console.log(3);
});
Promise.resolve().then(res => console.log(4))
console.log(5);
}
result()
console.log(6)
Promise.resolve().then(res => console.log(7))
setTimeout(() => {
console.log(8);
});