共计 3909 个字符,预计需要花费 10 分钟才能阅读完成。
后面面试的文章中咱们说过一道对于宏工作和微工作的题:
setTimeout(function(){console.log('1')
});
new Promise(function(resolve){console.log('2');
resolve();}).then(function(){console.log('3')
});
console.log('4')
试问一下下面代码的执行程序是啥?
有小伙伴可能会答:2,4,1,3
估摸着是这么想的:我难道不晓得 js 是一行一行执行的,setTimeout 是异步,所以先放前面;往下走,执行了 console.log(2),.then()是异步的,放在前面;走了 console.log(4); 再去异步队列里走,先是 console.log(1); 再是 console.log(3)。
But,事实真是如此吗?我有点慌,于是我粘贴到浏览器去瞅两眼:
我擦嘞,竟然是2,4,3,1!我几乎不敢相信!
带着困惑的我,只能去好好钻研钻研 JavaScript 的运行机制了!
1、对于 JavaScript
JavaScript 是一门单线程语言,即一次只能实现一个工作,若有多个工作要执行,则必须排队依照队列来执行(前一个工作实现, 再执行下一个工作)。
2、JavaScript 事件循环
既然 js 是 单线程,那就像只有一个窗口的食堂,学生须要排队一个一个打饭,同理 js 工作也要一个一个程序执行。这种模式执行简略,但随着日后的需要,事务,申请增多,这种单线程模式执行效率必然低下。只有有一个工作执行耗费了很长时间,在这个工夫里前面的工作无奈执行。
常见的有新闻蕴含的超清图片加载很慢,难道咱们的网页要始终卡着直到图片齐全显示进去?为了解决这个问题,JavaScript 语言将工作执行模式分成同步和异步:
同步模式: 就是下面所说的一种执行模式,后一个工作期待前一个工作完结,而后再执行,程序的执行程序与工作的排列程序是统一的、同步的。
异步模式: 就是每一个工作有一个或多个回调函数(callback),前一个工作完结后,不是执行后一个工作,而是执行回调函数,后一个工作则是不等前一个工作完结就执行,所以程序的执行程序与工作的排列程序是不统一的、异步的。
导图要表白的内容用文字来表述的话:
- 同步和异步工作别离进入不同的执行 ” 场合 ”,同步的进入主线程,异步的进入 Event Table 并注册函数。
- 当指定的事件实现时,Event Table 会将这个函数移入 Event Queue。
- 主线程内的工作执行结束为空,会去 Event Queue 读取对应的函数,进入主线程执行。
- 上述过程会一直反复,也就是常说的 Event Loop(事件循环)。
再配上代码表白:
let data = [];
$.ajax({
url:blog.csdn.net,
data:data,
success:() => {console.log('发送胜利!');
}
})
console.log('代码执行完结');
下面是一段繁难的 ajax
申请代码:
- ajax 进入 Event Table,注册回调函数
success
。 - 执行
console.log('代码执行完结')
。 - ajax 事件实现,回调函数
success
进入 Event Queue。 - 主线程从 Event Queue 读取回调函数
success
并执行。
置信通过下面的文字和代码,你曾经对 js 的执行程序有了初步理解。然而这也是为什么会有小伙伴答复 2,4,1,3 的起因。
然而实际上,异步队列里是还有门道的,咱们那道面试题,setTimeout 和 promise 的.then()都在异步队列了!接下来,讲讲那些门道 ( 宏工作和微工作)。
3、宏工作和微工作
每个人的了解形式不同,因为宏工作和微工作并不是规范,但执行的程序在 js 中是大一统了的。
宏工作
# | 浏览器 | Node |
<script> 整体代码 | √ | √ |
setTimeout | √ | √ |
setInterval | √ | √ |
setImmediate | x | √ |
requestAnimationFrame | √ | x |
Ajax | √ | √ |
DOM 事件 | √ | √ |
微工作
# | 浏览器 | Node |
process.nextTick | x | √ |
MutationObserver | √ | x |
Promise.then catch finally | √ | √ |
宏工作包含:<script> 整体代码、setTimeout、setInterval、setImmediate、Ajax、DOM 事件 \
微工作:process.nextTick、MutationObserver、Promise.then catch finallyprocess.nextTick 差别太大,不同的 node 执行不对立,不做规范 \
微工作比宏工作的执行工夫要早Tip: 有些人喜爱将 <script> 整体代码放在宏工作里,但我集体不喜爱,在我这里它只是第一执行的主线程,我集体是将宏工作和微工作都归类到异步工作里!
咱们再来看看那道面试题:
// 回调才是异步工作
setTimeout(function(){// 宏工作
console.log('1')
});
new Promise(function(resolve){console.log('2');// 同步主线程
resolve();}).then(function(){// 微工作
console.log('3')
});
console.log('4')// 同步主线程
2:同步中的第一个,故第一
4:同步中的第二个,故第二
3:异步中的微工作,故第三
1:异步中的宏工作,故第二
因而:2,4,3,1 的后果就进去了!
除此咱们来拓展一下:
setTimeout(() => {// 宏工作队列 1
console.log('1');// 宏工作队 1 的工作 1
setTimeout(() => {// 宏工作队列 3(宏工作队列 1 中的宏工作)
console.log('2')
}, 0)
new Promise(resolve => {resolve()
console.log('3')// 宏工作队列 1 的工作 2
}).then(() => {// 宏工作队列 1 中的微工作
console.log('4')
})
}, 0)
setTimeout(() => {// 宏工作队列 2
console.log('5')
}, 0)
console.log('6')// 同步主线程
执行整体代码(宏工作)console.log(‘6’) >> 宏工作队列 1、宏工作队列 2 位异步(顺次执行)
宏工作队列 1:=>
console.log(‘1’)
console.log(‘3’)
console.log(‘4’)// 宏工作中的微工作
剩下的不会先执行,因为是宏工作中的宏工作(console.log(2)),要被持续丢进工作队列后
宏工作队列 2:=>
console.log(‘5’)
宏工作队列 1 中的宏工作 3:=>
console.log(‘2’)
以上代码会怎么输入呢?
4、拓展宏工作微工作
下面出了简单的题,小伙伴们无妨能够想一想,这种简单状况,一个套一个的该怎么执行呢?
整体代码:
- 6:第一个同步主线程,故第一
script 整体代码里没有微工作故间接执行宏工作 =>
宏工作队列:
- 宏工作队列 1
工作 1:console.log(1)
工作 2:console.log(3)
宏工作队列 1 中的微工作:console.log(4)
宏工作队列 3:因他是宏工作队列 1 中的宏工作,所以被丢进了工作队列最初,咱们先看宏工作队列 1 同级的是否还有宏工作,有就先执行同级的,没有才能够执行宏工作队列 3!故 最初!
- 宏工作队列 2
console.log(5)
所以输入的后果是什么?是6,1,3,4,5,2!
通过验证,后果正确!
5、总结
实际上你只须要晓得拓展之前的,毕竟拓展后的的确比较复杂,须要肯定的理解能力,他能够三层,能够四层等 …. 我不信你会跟这题目一样写代码,这不是憨批是什么😆。
对于宏工作和微工作请记住这几点:
- 微工作比宏工作执行要早。
- 宏工作里如果有宏工作,不会执行外面的那个宏工作,而是被丢进工作队列前面,所以会最初执行。
此上就是我给大家的分享,如果有用,还望各位来个三连反对一下哟~~~感激🙏 各位了。
最初出个题:
console.log('1');
setTimeout(function() {console.log('2');
process.nextTick(function() {console.log('3');
})
new Promise(function(resolve) {console.log('4');
resolve();}).then(function() {console.log('5')
})
})
process.nextTick(function() {console.log('6');
})
new Promise(function(resolve) {console.log('7');
resolve();}).then(function() {console.log('8')
})
setTimeout(function() {console.log('9');
process.nextTick(function() {console.log('10');
})
new Promise(function(resolve) {console.log('11');
resolve();}).then(function() {console.log('12')
})
})
这题里有 process.nextTick,请在 nodejs 中验证哦~