事件循环
JavaScript 语言的一大特点就是单线程,为了主线程的畅通,EVENT LOOP 方案应运而生
前置知识点
- JS 有同步任务和异步任务两种
- 同步任务都在主线程上执行,形成一个同步任务执行栈
- 主线程之外,事件触发线程管理着一个任务队列,当异步任务有了运行结果,就往任务队列之中推入一个 task
- 当执行栈中的同步任务执行完毕,JS 引擎就会读取任务队列并将可运行的异步任务添加到执行栈中,开始执行
任务队列 [Task Queue]
事件循环是通过任务队列的机制来进行协调的,在一个事件循环中,可能有一个或者多个任务队列,一个任务队列便是一个有序任务(task)的集合
在事件循环中,每次循环操作成为 Tick,每个 Tick 的任务处理模型是比较复杂的,但是关键步骤【运行机制】大致如下:
- 执行一个宏任务(栈中没有就从事件队列中获取),例如:script
- 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
- 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(先进先执行)
- 当前宏任务&微任务队列执行完毕,开始检查渲染,然后 GUI 线程接管渲染
- 渲染完毕后,JS 线程继续接管,开始下一个宏任务(从事件队列中获取)【重复上述步骤】
参考示例图:
宏任务[Macrotask]
可以理解为:每次执行栈执行的代码就是一个宏任务
浏览器为了使宏任务与 DOM 任务能够有序的执行,在两次宏任务之间,会对页面进行一次更新渲染 render!
此处可以思考一下是不是为了在操作 dom 的时候可以取到正确的数据,etc...
宏任务有哪些:
- script(整体代码)
- setTimeout
- setInterval
- UI 交互事件
- I/O
- postMessage
- MessageChannel
- setImmediate(Node.js)
微任务[Microtask]
可以理解为:在当前的 task 执行结束后立即执行的任务。也就是说,在当前 task 执行栈之后,下一个 task 之前,在渲染之前
思考一下:在一个宏任务执行完后,会将该任务执行期间产生的所有微任务都执行完毕(在渲染前),再去渲染,再执行下一个宏任务?
Promise 的说明
Promise 的处理程序.then、.catch、.finally 都是异步的,即便一个 promise 立即 resolve,处理程序也需要等宏任务执行完才会执行。其实 ECMA 标准规定了一个内部队列 PromiseJobs,通常称之为"微任务队列",Promise 的处理程序会被放入到该队列中,当 JS 引擎执行完当前的代码,才会从微任务队列获取任务并执行。
微任务有哪些:
- Promise
- async/await
- Object.observe
- MutationObserver
- process.nextTick(Node.js)
思考一下下面这个程序的输出:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。