1.javascript的单线程
javascript是单线程的。
单线程的意思就是说负责解释和执行javascript代码的线程只有一个,同一个时间内只能执行特定的一段js代码(当前任务)。
为什么javascript是单线程的?
1.js最初被设计出来及时作为一种浏览器脚本,主要负责和用户交互,操作dom.
2.如果js是多线程的,那么就会带来非常复杂的同步问题:
1.比如有两个线程,同时对一个dom操作,一个线程要删除,一个线程要在它上面添加属性,此时就有点不知所措
2.有机制可以解决这种问题,但问题的复杂度也会上升
2.Web Workers
随着web应用规模越来越大,越来越复杂的计算在所难免。
如果长时间运行一段复杂的js代码,会导致进程被阻塞,导致浏览器冻结用户界面,影响用户体验。
所以HTML5制定了Web Workers标准。
允许javascript创建多个线程,但是这些子线程会受到主线程的控制,且它们不得对DOM进行操作。
这样的话本质上并没有改变js单线程的本质 ,而且可以更好的利用多核CPU的性能,提高用户体验。
3.单线程的javascript如何实现异步?
虽然负责解释和执行javascript代码的线程的只有一个。
但浏览器内部还有其他线程来专门负责异步任务的,比如定时器,UI,事件,网络之类专门的线程来负责相关任务的处理。
4.实现的逻辑(event loop)
1.javascript主线程会维护一个执行栈,然后逐行执行代码。
2.如果碰到异步代码(比如Promise.then,AJAX,setTimeout等等),那么它会把这些代码交给浏览器的其他相关的线程去执行,然后跳过这些代码继续往下执行,直到
当前代码段结束。
3.同时浏览器的其他相关线程处理好该异步代码执行前需要的准备工作时(比如说AJAX数据,鼠标事件等触发,定时器的时间到了等)浏览器会把当前的异步代码放入一
个异步任务队列中(queue先进先出)
4.每次当主线程逐行执行完当前代码时,都会去检查异步队列中是否有任务需要执行。如果有,那么就把那个任务拿出来再逐行执行,如果在其中有碰到了异步代码,那么再
交给浏览器的其他线程去处理,然后跳过这些代码继续执行,直到当前代码结束。然后检查异步队列,如此循环往复,所以叫事件队列。
5.异步队列:Microtask(微任务)和Macrotask(宏任务)
异步队列主要分为Microtask(微任务)和Macrotask(宏任务)队列。
Microtask(微任务):
- process.nextTick
- Promise
- Object.observe
- MutationObserve
Macrotask(宏任务):
- setTimeout
- setInterval
- setImmediate
- I/O
- UI渲染
- <script>中的js代码
6. Microtask(微任务)和Macrotask(宏任务)队列执行顺序
宏任务队列和微任务队列依次执行,但是有一个重要的区别
当前微任务队列中的任务会被全部执行完。
当前宏任务队列中的任务只会挑队列中一个最先的任务执行,然后就跳到微任务。
7.描述微任务与宏任务交替执行
<script>中的js代码会作为第一个宏任务执行。
因为队列是依次执行的,所以执行完宏任务会再执行微任务。
宏任务只会先执行最先的一个,再跳到微任务,把所有微任务都执行掉再跳到宏任务执行最先的一个。
8.任务的优先级
idle观察者>I/O观察者>check观察者
process.nextTick(idle) > Promise(原生实现)
setTimeout(I/O) > setImmediate(check)
这个优先级会有差异,但如果时间为0,基本符合这个结果
9.任务优先级执行示例
宏任务:先执行<script>中的代码,同步执行,因为promise构造函数是同步的,所以打印3,4,逐行执行打印6,8。
微任务:因为process.nextTick大于promise,所以打印7;然后执行promise.then方法,打印5。
宏任务:因为setTimeout(I/O) > setImmediate(check),所以打印2;完了之后跳到微任务队列,但微任务队列任务执行完了就不管了,直接直接宏任务,然后打印1.
setImmediate(function(){
console.log(1)
},0)
setTimeout(function(){
console.log(2);
},0);
new Promise(function(resolve){
console.log(3);
resolve();
console.log(4);
}).then(function(){
console.log(5);
});
console.log(6);
process.nextTick(function(){
console.log(7);
});
console.log(8);
//执行结果:3,4,6,8,7,5,2,1
该博文参考改编自b站视频:https://b23.tv/BV1XW411H7X1