node.js是什么
是一个基于Chrome V8引擎的JavaScript运行环境
1.非阻塞
Node.js标准库中的所有 I/O 方法都提供异步版本,即非阻塞,并接受回调函数。一些方法还具有阻塞对应项,其名称以 .Sync。
2.V8引擎
blink渲染,
3.线程与事件循环
进程 - 系统资源分配,有自己独立的地址空间,一个进程有多个线程,分别执行不同任务。进程之间不能共享资源,而线程共享所在进程的地址空间和其它资源。同时线程还有自己的栈和栈指针,程序计数器等寄存器。
线程 - cpu调度单位,它包含在进程中,是进程运行的最小单位,线程依赖进程存在,线程的切换开销相对较小
node得运行机制
主线程一个,底层工作线程有多个。
js通过V8引擎调用node API,node API底层由c++ libuv库实现,libuv库将接收到的不同异步操作分配给不同的线程,不同线程处理结束后已异步方式,将结果返回给v8引擎。
js任务分为同步任务和异步任务,异步任务分为微任务与宏任务。
1、主线程执行栈全部任务执行完毕。
2、检查微任务队列,process.nextTick优先级最高,总是最先执行。
3、检查宏任务队列,提取一次任务推入执行栈,进行执行。
右侧宏任务队列才是事件循环的关键。事件循环就是为了解决异步操作。所以同步任务不属于事件循环,同时微任务也不属于事件循环的一部分。
事件循环如图分为6个阶段,每个阶段为一个FIFO回调队列(可理解为回调函数数组),按图顺序依次执行。事件循环每进入一个阶段,则将该阶段的回调队列用尽(全部执行完毕)或到最大限制回调数,则进入下一阶段。
列:
//宏任务 check阶 暂时命名任务1 setImmediate方法用于将任务放入事件循环中的check阶段
setImmediate(()=>console.log(1))
//宏任务 timer阶段暂时命名任务2
setTimeout(()=>{
console.log(2);
//宏任务 timer阶段 时命名任务22
setTimeout(()=>{
console.log(22)
},1.0);
//宏任务 timer阶段暂时命名任务3
setTimeout(()=>{console.log(3);})
},1.8);
//宏任务 timer阶段暂时命名任务4
setTimeout(()=>{
console.log(4);
},100);
//微任务
//暂时命名任务5
process.nextTick(()=>console.log(5));
//同步任务
//暂时命名任务6
console.log(6);console.log(7);//执行结果为 6 7 5 2 3 1 22 4
步骤1 首先根据js执行顺序,从上到下依次执行,发现宏任务将任务放入下一个事件循环中,发现微任务,将其置入微任务队列中。将同步任务置入执行栈中。
步骤2 开始运行执行栈,其中有两行同步语句,输出 6 7,此时执行栈中运行完毕,主线程空闲;
步骤3 开始检查微任务队列,微任务队列中包含 process.nextTick,则输出5,微任务队列执行全部执行完毕,队列中再无其他微任务,则检查宏任务队列开始进行事件循环。
步骤4 事件循环进入第一阶段(timer阶段),检查是否有定时器超时的回调,此时发现队列中有两个回调(任务2和任务3回调)。
先执行任务2回调,输出 2。发现宏任务22,则将该任务放入下一次事件循环中(同步骤1。也同时符合执行一个宏任务则继续运行执行栈和微任务的解释)。
步骤5 任务2回调执行完毕,继续执行任务3回调。 (注意所有异步操作是后台操作完成可以触发回调函数时,才将回调函数放入对应阶段的队列中。所以此时timer队列中并无任务4回调) 。
步骤6 timer队列清空,事件循环进入下一阶段,依次到check阶段之前均为空队列,进入check阶段,执行任务1回调,输出1。继续进入close callback阶段。结束本次事件循环。
步骤7 进入下一个事件循环,timer阶段,队列中任务22回调,输出22。继续进入下面阶段,直到循环结束。
继续进入下一循环。。直到任务4超时回调触发 输出 4。