微任务和宏任务主要区别在于他们的执行顺序,Event Loop的走向和取值。
宏任务和微任务皆为异步任务,它们都属于一个队列。宏任务:当前调用栈中执行的代码成为宏任务。(主代码快,定时器等等)。微任务: 当前(此次事件循环中)宏任务执行完,在下一个宏任务开始之前需要执行的任务,可以理解为回调事件。。 宏任务中的事件放在callback queue中,由事件触发线程维护;微任务的事件放在微任务队列中,由js引擎线程维护。
在执行栈中执行一个宏任务,执行过程中遇到微任务,将微任务添加到微任务队列中。当前宏任务执行完毕,立即执行微任务队列中的任务。当前微任务队列中的任务执行完毕,检查渲染,GUI线程接管渲染。渲染完毕后,js线程接管,开启下一次事件循环,执行下一次宏任务(事件队列中取)。
异步任务定义
说到异步任务,我们第一时间想到的是多线程,我们常说的多线程问题,一般是指线程并发问题和数据同步问题。而异步任务属于多线程编程的一个环节:主线程在继续当前任务的同时,创建一个或多个新的非阻塞线程去执行其他任务。
当主流程外需要执行1个或多个复杂计算,为了保障执行效率,可以使用异步任务。异步任务的主要作用是最大程度地使用服务器的性能,提升程序的响应速度。无需获取响应结果的简单异步 如:消息通知。需要响应结果的异步 如:商品信息查询。
运行下面例子分析:
打印顺序依次为:
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引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行。
1.宏观任务(macro task): 由宿主(node、浏览器) 发起的任务,如setTimeOut、setInterval、setImmediate、I/O
2.微观任务(micro task):由 js引擎 发起的任务,如process.nextTick、promise、mutationObserver