本文解析JavaScript单线程模型下的Event Loop机制,区分浏览器与Node.js环境差异。详细阐述宏任务(如setTimeout)与微任务(如Promise)的队列特性及执行优先级,结合Node.js中timers与setImmediate的时序关系,通过代码示例说明事件循环的具体运行步骤。
环境判断与Event Loop阶段
在深入探讨执行机制前,首先需要明确当前代码运行的环境是浏览器还是Node.js。
区分这两种环境的一个简单方法是观察全局对象。在Node.js环境中,全局变量window未定义,但setImmediate函数已被定义;而在浏览器环境中,情况则相反,setImmediate通常未定义。
Node.js的Event Loop包含多个阶段,其中timer阶段专门负责处理setTimeout和setInterval回调函数的执行。
check阶段则用于执行通过setImmediate注册的回调函数。
宏任务与微任务的分类及特性
从任务分类来看,宏任务(Macro Task)主要包括初始执行的script代码块、setTimeout、setInterval、I/O操作以及UI交互事件(如点击事件)。
微任务(Micro Task)则包括Promise相关的回调、process.nextTick以及Promise().then()中注册的函数。
在队列管理上,宏任务可以存在多个不同的任务队列。
相比之下,微任务始终只维护一个单一的队列。
对于setTimeout等定时器任务,其推迟执行的毫秒数越小,该任务在队列中的排序就越靠前,从而更早被调度执行。
值得注意的是,在Node.js环境中,timer阶段(处理setTimeout和setInterval)的执行优先级高于check阶段(处理setImmediate)。
setTimeout(() => {
console.log('setTimeout')
},0); // 大于1000时,会先执行setImmediate
setImmediate(()=> { console.log('setImmediate')})
console.log('start');
setTimeout(function (){
console.log('timeout');
},10);
new Promise((resolve) => {
console.log('promise');
resolve()
setTimeout(() => {
console.log('Promsie中的setTimeout');
},0);
}).then(() => {
console.log('then');
});
console.log('end');
事件循环运行机制详解
以下是JavaScript事件循环的核心运行机制:
第一步,JavaScript引擎在执行栈中执行一个宏任务。
在执行过程中,如果遇到微任务,则将其添加到微任务队列中等待处理。
当当前宏任务执行完毕后,引擎会立即清空并执行微任务队列中的所有任务。
微任务队列执行完毕后,系统会检查是否需要渲染页面,此时GUI线程接管进行渲染工作。
渲染完成后,JavaScript线程重新接管控制权,开启下一次事件循环,从事件队列中取出下一个宏任务继续执行。
以上即为JavaScript Event Loop机制的核心内容,希望这些解析能帮助大家更好地理解单线程模型下的异步执行逻辑。