参考:https://www.cnblogs.com/amiezhang/p/11349450.html
https://juejin.im/post/5b73d7a6518825610072b42b
宏任务(同步任务)->微任务->宏任务(异步任务)
1、背景
js是单线程的,同一时间只能做一件事,为了协调事件、用户交互、脚本、UI渲染和网络处理等行为,防止主线程的阻塞。
js可分为同步任务和异步任务,对于同步的任务,我们是按顺序进行执行,但是对于异步的操作,会有一个优先级的执行顺序,分别为宏任务和微任务。
在js执行时,一个主线程里面都会有一个事件循环(消息循环|运行循环)和事件队列,存放各种要处理的事件信息,通过这个循环不断处理这些事件信息或消息。
2、JavaScript的运行分为
(1) javaScript Engine,Chrome 的引擎就是 V8
(2) Web APIs,DOM 的操作,AJAX,Timeout 等实际上调用的都是这里提供的
(3)Callback Queue,回调的队列,也就是刚刚所有的 Web APIs 里面的回调函数,实际上都是放在这里排队的
(4) EventLoop,事件循环,也就是刚所说的宏任务和微任务的容器
3、事件循环EventLoop
Js的本质是单线
1、一般而言,非阻塞性的任务采用同步的方式,直接在主线程的执行栈中完成。
2、一般来说,阻塞性的任务都会采取异步来执行,异步的工作一般会交给其他线程完成,然后回调函数会放在事件队列中。
时间循环的过程
1、检查还有没有微任务需要处理
2、结束本次宏任务,检查还有没有宏任务需要处理
3、循环这些操作就是event-loop
当主线程的任务执行完了(执行栈空了),JS会去询问事件队列
执行一个宏任务(先执行同步代码)-->执行所有微任务-->UI render-->执行下一个宏任务-->执行所有微任务-->UI render-->......
根据HTML Standard,一轮事件循环执行结束之后,下轮事件循环执行之前开始进行UI render。即:macro-task任务执行完毕,接着执行完所有的micro-task任务后,此时本轮循环结束,开始执行UI render。UI render完毕之后接着下一轮循环。但是UI render不一定会执行,因为需要考虑ui渲染消耗的性能已经有没有ui变动
4、call Stack
- 本身就是一个调用栈(就像浏览器中的JavaScript解释器),追踪函数执行流的一种机制,当执行环境调用了多个函数时,通过调用栈,我们可以追踪到哪一个函数在执行,执行的函数体中又调用了哪些函数。
- 每调用一个函数,解释器就会把该函数添加进调用栈并开始执行。
- 正在调用栈中执行的函数还调用了其它函数,那么新函数也将会被添加进调用栈,一旦这个函数被调用,便会立即执行。
- 当前函数执行完毕后,解释器将其清出调用栈,继续执行当前执行环境下的剩余的代码。
- 当分配的调用栈空间被占满时,会引发“堆栈溢出”。
- 是存放执行的重要条件,也是因为只有一个调用栈,所以被称为单线程
5、宏任务(macrotasks)
- macrotasks: setTimeout, setInterval, setImmediate, I/O, UI rendering
- 任务队列中的都是已经完成的异步操作,而不是说注册一个异步任务就会被放在这个任务队列中,就像在银行中排号,如果叫到你的时候你不在,那么你当前的号牌就作废了,柜员会选择直接跳过进行下一个客户的业务处理,等你回来以后还需要重新取号
- 一个宏任务在执行的过程中,是可以添加一些微任务的,且在当前的微任务没有执行完成时,是不会执行下一个宏任务的。
setTimeOut是个宏任务,而Promise.then则是微任务,输出是2,4,3,1
所有会进入的异步都是指的事件回调中的那部分代码
- new Promise在实例化的过程中所执行的代码都是同步进行的,而then中注册的回调才是异步执行的。
- 在同步代码执行完成之后才回去检查是否有异步任务完成,并执行对应的回调,而微任务又会在宏任务之前执行。
- +部分表示同步执行的代码
+setTimeout(_ => { - console.log(4) +}) +new Promise(resolve => { + resolve() + console.log(1) +}).then(_ => { - console.log(3) +}) +console.log(2)
setTimeout(_ => console.log(4)) new Promise(resolve => { resolve() console.log(1) }).then(_ => { console.log(3) Promise.resolve().then(_ => { console.log('before timeout') }).then(_ => { Promise.resolve().then(_ => { console.log('also before timeout') }) }) }) console.log(2)
6、微任务(microtasks)
- microtasks: process.nextTick, Promises, Object.observe(废弃), MutationObserver