1.node是什么?
node是一个基于Chrome V8引擎的javascript运行环境,node不是一门语言,而是让javascript运行在后端的运行时环境,因此node中没有DOM和BOM,node也提供了一些内置模块,例如:http,fs等。nodejs使用了事件驱动、非阻塞式I/O的模型,使其轻量又高效。
2.进程和线程
一个进程中能够包含多个线程。例如浏览器的渲染引擎,其实就是浏览器内核,它内部就是多线程的,内部包含两个很是重要的线程UI线程和JS线程,特别要注意的是UI线程和JS线程是互斥的,由于JS运行结果会影响到UI线程的结果,那么UI更新会被保存在队列中,等到JS线程空闲时,当即被执行。
3.浏览器中的Event Loop(事件循环)
咱们从一道面试题,说明一下浏览器的event loop:
console.log(1);
setTimeout(function(){
console.log(2);
});
console.log(3);
复制代码
咱们你们都知道,运行结果是1,3,2。可是,浏览器究竟是怎么执行的呐?
由于console.log(1)和console.log(3)是同步任务,因此将它们放到运行栈中去执行,js引擎在遇到setTimeout时,会把它看成异步任务,不会把它放到运行栈中去执行,浏览器的timer模块会把setTimeout先拿走,时间到了,timer模块会把它放到异步队列中去,js引擎发现运行栈中没有要执行的东西了,就会读取异步队列中的内容,放到运行栈中去执行,这时setTimeout中的函数体就变成运行栈中的同步任务,执行完后,再去监听异步队列中有没有,若是有继续执行,如此循环,这个循环的过程就是event loop。
4.node系统
咱们写的js代码会交给V8引擎进行处理;代码中可能调用node API,node会交给libuv库处理;libuv经过阻塞I/O和多线程实现了异步I/O;经过事件驱动的方式将结果放到事件队列中,最终交给咱们的应用。
5.宏任务和微任务
任务分为宏任务和微任务。
macro-task(宏任务):setTimeout,setInterval,setImmediate,I/O
micro-task(微任务):原生Promise,process.nextTick,MessageChannel(vue中nextTick实现原理)
在浏览器中,先执行当前栈,执行完后,再走微任务,微任务执行完后,再去取事件队列中的内容。
console.log(1);
console.log(2);
setTimeout(function(){
console.log('setTimeout1');
Promise.resolve().then(function(){
console.log('promise');
});
});
setTimeout(function(){
console.log('setTimeout2');
});
复制代码
浏览器中的运行结果是:
1
2
setTimeout1
promise
setTimeout2
node中的运行结果:
1
2
setTimeout1
setTimeout2
promise
咱们发现,一样的代码,在浏览器中运行和在node中运行的结果不一样,这是为何呐?接下来,我就要说一下node中的event loop。
在libuv内部,有这样一个事件环机制,在node启动时,会初始化事件环。
这里每个阶段都对应一个事件队列,当event loop执行到某个阶段时,会将当前阶段对应的队列依次执行完。当队列执行完毕或执行的数量超过上限时,会转入下一个阶段。
那就说一下上题,在node中的执行的详细过程吧。
首先,将console.log(1)和console.log(2)放到运行栈中去执行,执行完后,会看一下是否有微任务,若是有,会执行微任务(微任务执行,都会在阶段转换时被执行),那么如今没有,接着往下执行,遇到两个setTimeout,会将它们放到第一阶段timers(计数器)中,接着往下一个阶段执行,当setTimeout到时间了,会将到时间的setTimeout都执行完毕,再去执行微任务,当执行第一个setTimeout时,遇到了一个Promise微任务,会将它放到微任务中,而后,执行第二个setTimeout,都执行完后,再去执行微任务。
咱们再看几个题,可以更好的理解node中的event loop。
process.nextTick(function(){
console.log("nextTick");
});
setImmediate(function(){
console.log("immediate");
});
复制代码
node中的运行结果,毫无疑问是
nextTick
immediate
let fs = require('fs');
fs.readFile('./1.log' , function(){
console.log('fs');
setTimeout(function(){
console.log('timeout');
});
setImmediate(function(){
console.log('immediate');
});
});
复制代码
node中的运行结果是:
fs
immediate
timeout
由于I/O操做完了,会走check阶段,因此setImmediate会早于setTimeout。
再看一下,最后一道题,这道题你答对了,说明你真的明白了node中的event loop了。
setImmediate(function(){
console.log(1);
process.nextTick(function(){
console.log(4);
});
});
process.nextTick(function(){
console.log(2);
setImmediate(function(){
console.log(3);
});
});
复制代码
node中的运行结果:
2
1
3
4
你答对了吗?