我们知道 JavaScript是单线程的,就像上面故事的老板,他得去服务员去招待客人点菜,并将菜单给厨师,厨师炒好后再给到他去上菜。如果老板不请个厨师,自己来炒菜的话,那么在炒菜时就没办法接待客人,客人就会等待点菜。等着等着就会暴露出服务态度不行的问题。所以说,得有厨师专门处理炒菜的任务
所以在js中,任务分为同步任务和异步任务,
同步任务 -> 服务员去接待客人点菜
异步任务 -> 厨师炒菜、异步回调函数相当于 服务员去上菜
JS的事件循环如图所示,
在执行主线程的任务时,如果有异步任务,会进入到Event Table并注册回调函数,当指定的事情完成后,会将这个回调函数放到 callback queue 中
在主线程执行完毕之后,会去读取 callback queue中的回调函数,进入主线程执行
不断的重复这个过程,也就是常说的Event Loop(事件循环)了
2. 异步任务异步任务又分为宏任务跟微任务、他们之间的区别主要是执行顺序的不同。
在js中,微任务有原生的Promise -> 其实就是我们上面提到的VIP用户,
process.nextTick -> 其实就是我们上面提到的超级VIP用户,
process.nextTick的执行优先级高于Promise的
宏任务整体代码 script
setTimeout -> 其实就是我们上面提到的普通用户,
setImmediate -> 其实就是我们上面提到的群体用户,
setTimeout的执行优先级高于 setImmediate 的
宏任务与微任务的执行过程在一次事件循环中,JS会首先执行 整体代码 script,执行完后会去判断微任务队列中是否有微任务,如果有,将它们逐一执行完后在一次执行宏任务。如此流程
测试下面我们来看一段代码是否了解了这个流程
<script> setTimeout(() => { console.log('a'); new Promise( res => { res() }).then( () => { console.log('c'); }) process.nextTick(() => { console.log('h'); }) }, 0) console.log('b'); process.nextTick( () => { console.log('d'); process.nextTick(() => { console.log('e'); process.nextTick(() => { console.log('f'); }) }) }) setImmediate( () => { console.log('g'); }) </script>执行结果为:b d e f a h c g
让我们来分析一下这段代码的执行流程
首页执行第一个宏任务 整段script标签代码,遇到第一个 setTimeout,将其回调函数加入到宏任务队列中,
输出 console.log('b')
遇到process.nextTick,将其回调函数加入到微任务
遇到setImmediate 将其回调函数加入到宏任务队列中
宏任务Event Queue 微任务Event QueuesetTimeout process.nextTick
setImmediate
当第一个宏任务执行完后,就会去判断是否还有微任务,刚好有一个 微任务,执行process.nextTick的回调,输出 console.log('d'),然后又遇到了一个process.nextTick,又将其放入到微任务队列
继续将微任务队列中的回调函数取出,继续执行,输出 console.log('e'),然后又遇到了一个process.nextTick,又将其放入到微任务队列
继续将微任务队列中的回调函数取出,继续执行,输出 console.log('f'),然后又遇到了一个process.nextTick,又将其放入到微任务队列
宏任务Event Queue 微任务Event QueuesetTimeout
setImmediate
当微任务队列为空后,开始新的宏任务,取出第一个宏任务队列的函数,setTimeout,执行 console.log('a'),然后遇到Promise,process.nextTick 将其回调加入到微任务队列。执行完后
宏任务Event Queue 微任务Event QueuesetImmediate promise.then
- process.nextTick