Nodejs的单线程是指Javascript运行在单线程中,而不是Nodejs是单线程
js工作机制:
-
当线程中没有执行任何同步代码的前提下才会执行异步代码
-
一个浏览器环境只能有一个事件循环,一个事件循环可以有多个任务队列
-
事件优先级顺序
process.nextTick > promise.then > setTimeout > setImmediate
- Event Loop(时间循环)处理异步任务,分为两种 宏任务(MacroTask)和微任务(MicroTask),在执行宏任务之前会先清空微任务。
术语:
macrotask queue 宏任务队列
setImmediate
setTimeout //有一个最小延迟4ms
setInterval
microtask queue 微任务队列
process.nextTick
Promise
MutaionObserver
例1:
该程序会陷入一个死循环,setTimeout属于异步操作,只有当js空闲才会执行,但死循环是不会空闲的,因此setTimeout也将永远不会执行
<script>
var t = true;
setTimeout(function (){
t = false;
console.log('setTimeout', t);
},1000);
while (t){
}
console.log('end');
</script>
例2:
javascript是单线程,只有同步代码执行完毕后,才会去执行异步代码,下面的例子中每次for循环javascript都会挂起这个异步操作,
setTimeout会进入一个任务队列,继续执行下面的代码,等到同步代码执行完毕后,系统开始读取挂起来的异步操作(异步的任务队列),
也就是这些异步任务指定的回调函数
<script>
//如果是var声明会输出5个5,解决这个问题,在代码块中使用闭包或者let或const声明都可以
for (let i = 0; i < 5; i++) {
console.log(i);
setTimeout(function() {
console.log('setTimeout: ', i);
},1000 * i);
}
/**
* 输出结果:
* 0
* 1
* 2
* 3
* 4
* setTimeout: 0
* setTimeout: 1
* setTimeout: 2
* setTimeout: 3
* setTimeout: 4
*/
</script>
例3
process.nextTick与setTimeout递归调用区别
问题出自 ElemeFE
process.nextTick属于微任务,是在当前执行栈的尾部,EventLoop之前触发,下面两个都是递归调用,test1中process.nextTick,是在当前执行栈调用,是一次性执行完,相当于 while(true){},主线程陷入了死循环,阻断IO操作。
test2方法中,setTimeout属于宏任务,在任务队列中,同样也是递归不是一次性的执行而是在多次Loop,不会阻断IO操作,另外注意setTimeout有一个最小的时间4ms。
function test1() {
process.nextTick(() => test());
}
function test2() {
setTimeout(() => test(), 0);
}
process.nextTick将会阻塞IO,setImmediate不会输出
{
function test() {
return process.nextTick(() => test());
}
test();
setImmediate(() => {
console.log('setImmediate');
})
}
下面使用setTimeout不会造成IO阻塞,会输出 setImmediate
function test() {
setTimeout(() => test(), 0);
}
test()
setImmediate(() => {
console.log('setImmediate');
})
// setImmediate
例4 setImmediate与setTimeout
setImmediate(
function(){ console.log(1); }
,0);
setTimeout(
function(){ console.log(2); }
,0);
// 浏览器环境运行结果
// 1
// 2
// Nodejs环境运行结果
// 2
// 1
setImmediate(function A() {
console.log(1);
setImmediate(function B(){console.log(2);});
});
setTimeout(function timeout() {
console.log('TIMEOUT FIRED');
}, 0);
// 浏览器环境运行结果
// 1
// 2
// TIMEOUT FIRED
// Nodejs环境运行结果两种情况
// 1
// TIMEOUT FIRED
// 2
// TIMEOUT FIRED
// 1
// 2