1.线程与同异步
首先,js只能是单线程语言,因为他是操作dom的话多线程是操作不了的。
单线程意味着所有代码执行必须是一一先后执行。
但若有像定时器那样的任务就会影响后续代码的执行影响dom渲染。
所以js将任务机制分为同步任务和异步任务,任务在主线程(刚刚说的一条线程中)执行,
同步的按顺序执行,异步的就先交给 其他模块 处理,有结果后 再把回调函数拿到 task Queue 任务队列中等待同步任务进行完后再执行。
2.先不谈宏任务和微任务,先分同步任务和异步任务去做个初步理解
3.现在把这个总的任务队列(task Queue)分为两种 :
宏任务队列和微任务队列。
先说一下宏任务和微任务的区分方法
3.1.宏任务:通过浏览器提供的API 调用的方法。
如DOM操作,console.log打印方法、setTimeout/interval还有ajax请求。
这些方法都不是js里面的方法,而是用了浏览器的 API才能去调用的(浏览器提供的方法)
另外解释下dom操作:
其实javaScript能操作dom完全是因为浏览器给予了js操作 dom的接口罢了,不过也是,js本来就是个浏览器脚本语言。
3.2.微任务:JavaScript调用自己内部API的方法。
如Promise、MutationObserver、process.nextTick
4.再细分:
其实看了有些人发的博客发现在 宏任务上容易让人误解:异步任务中分两类 一个宏任务一个微任务,那是不是宏任务就是异步任务?
但其实宏任务中分两部分:同步部分和异步部分,
宏任务的同步部分有 console.log、dom操作
宏任务的异步部分有 setTimeout/interval、ajax请求等
微任务也有两部分: 同步部分和异步部分,
像promise这种,他按照API来分类是微任务。
微任务的同步部分有:promise本身包裹的函数体是立即执行的,属于同步部分。
微任务的异步部分有:而他用then来执行的回调函数就是是异步部分。
4.大致顺序
1.js代码都进入执行栈中
2.其中宏任务和微任务的同步部分 ==> 仍留在stack执行栈等待执行。
-
宏任务中的异步部分==>浏览器处理模块==>完成处理并将回调函数==>宏任务队列。
-
微任务中的异步部分==>JavaScript内部处理模块==>完成处理并将回调函数==>微任务队列。
像promise的then中的回调函数,就是被压入微任务队列中的。
5.宏微执行顺序以及图解:
1.宏任务和微任务的同步部分在stack中立即执行。(宏同+微同)
2.同步部分的最后一个函数执行完(或者说stack为空时)才会去搜索微任务队列 有无微任务,有的话全部压如stack执行,执行完之后去宏任务队列执行一个宏任务(把这个宏任务压到stack中执行),执行完这个宏任务就去搜索微任务队列 有无微任务,有的话全部执行,执行完之后…后面就是一个个循环
6.eventLoop:
如果把宏任务微任务的执行写一个循环图的话,注意将宏同+微同的最后一个函数 理解为特殊的开头宏任务。或者将宏同+微同理解为头宏任务队列,只不过这个队列只有执行完最后一个函数才进入宏微循环。(毕竟这么顺畅的同步任务也不会再插入循环判断破坏性能了)
这个循环我们就叫他事件循环(eventLoop)
-
6.1 eventLoop每一轮的结束标志:stack为空。
-
6.2 并且 重新渲染也是在一个循环(tick)结束后进行。
7.巩固练习:
setTimeout(() => {
console.log(‘宏异’)
}, 0)
console.log(‘宏同’);
new Promise((resolve) => {
console.log(‘微同’);
resolve()
}).then(() => {
console.log(‘微事件1(微异)’);
}).then(()=>{
console.log(‘微事件2(微异)’)
})
8.打印结果:
.
9.不同理解存疑讨论 :
其实我看到也有博客说 此处的 宏同+微同 部分就是第一个宏任务队列,但我根据API定义来判别 如果有promise的话 解析到promise时就是微任务了。
而且第一个宏任务队列的内部执行是只执行这些宏事件但不遵循宏-微循环。
所以我认为这个特殊的宏任务队列单独拎出来 管它叫 宏同+微同 好一些,欢迎大家质疑讨论发表见解!