一、抛出JS机制、任务 (task)
js的核心特征是单线程,意味着所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。于是就有了现在的两种任务类型:同步任务(synchronous),和异步任务(asynchronous)。
同步任务指在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
二、了解异步执行运行机制
tips: 同步执行可被视为没有异步任务的异步执行,执行也是如此。
- 所有同步任务都在主线程(main thread)上执行,形成一个执行栈(execution context stack)。
- 主线程之外,存在一个"任务队列(task queue)"。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
- 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
- 主线程不断重复第三步。
"任务队列"是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,执行栈一清空,"任务队列"上第一位的事件自动进入主线程。但是由于"定时器"的存在,主线程首先要检查一下执行时间,某些事件到规定的时间,才能返回主线程。
堆(heap)、栈(stack)、事件循环(Event Loop)
三、引出宏任务和微任务
进入正题,除了广义的同步任务和异步任务,我们对任务有更精细的定义如下,微任务的优先级比宏任务高,只要微任务队列里面有任务,宏任务队列永远得不到执行:
宏任务(Macrotask) | 微任务(Microtask) |
---|---|
setTimeout | process.nextTick(Node环境) |
setInterval | MutationObserver(浏览器环境) |
MessageChannel | Promise[ then / catch / finally ] |
setImmediate(Node环境) | |
script(整体代码块) | |
I/O操作 | |
UI渲染 |
- 宏任务优先级:主代码块 > setImmediate > MessageChannel > setTimeout / setInterval>I/O>UI rendering
- 微任务优先级:process.nextTick > Promise > MutationObserver
tips:另外还有一个requestAnimationFrame