宏任务:
1.发起者:node,浏览器;
2.事件:script(整体代码)、setTimeout、setInterval、setImmediate(Node.js 环境)、UI事件、I/O(Node.js);
3.运行:后运行
微任务:
1.发起者: JS引擎;
2.事件:Promise、MutaionObserver、process.nextTick(Node.js);
3.运行:先运行;
运行下面的例子:
console.log('script start') async function async1() { await async2() console.log('async1 end') } async function async2() { console.log('async2 end') } async1() setTimeout(function() { console.log('setTimeout') }, 0) new Promise(resolve => { console.log('Promise') resolve() }) .then(function() { console.log('promise1') }) .then(function() { console.log('promise2') }) console.log('script end')
script start
async2 end
Promise
script end
async1 end
promise1
promise2
setTimeout
结果解析:
JS 运行机制为从上而下,首先打印script start,async/await 是Promise包装执行会立即执行async2()打印async2 end,把setTimeout加入宏任务队列但是不会立马执行,往下走,Promise创建属于同步任务此时打印Promise,并放入宏任务队列中,再往下走直接打印script end,此时如果有微任务会优先执行微任务队列,微任务队列执行完毕再执行宏任务,async1 end 的打印是因为执行了Promise.then的方法,继续微任务输出promise1、promise2,当微任务队列为空,执行宏任务setTimeout,此时打印setTimeout。
说说事件循环(Event Loop)
一个 Event Loop 中,可以有一个或者多个任务队列(task queue),一个任务队列便是一系列有序任务(task)的集合;每个任务都有一个任务源(task source),源自同一个任务源的 task 必须放到同一个任务队列,从不同源来的则被添加到不同队列。setTimeout/Promise 等API便是任务源,而进入任务队列的是他们指定的具体执行任务。
在事件循环中,每进行一次循环操作称为 tick,每一次 tick 的任务处理模型是比较复杂的,
关键步骤如下:
在此次 tick 中选择最先进入队列的任务(oldest task),如果有则执行(一次)
检查是否存在 Microtasks,如果存在则不停地执行,直至清空 Microtasks Queue
更新 render
主线程重复执行上述步骤
在上诉tick的基础上需要了解几点:
1、JS分为同步任务和异步任务
2、同步任务都在主线程上执行,形成一个执行栈
3、主线程之外,事件触发线程管理着一个任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件。
4、一旦执行栈中的所有同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行。
作者:宋小菜_菜菜
链接:https://www.jianshu.com/p/5b4c4756e461
来源:简书