事件循环机制
事件循环机制(Event Loop) JavaScript为单线程,怎样处理任务
- 处理安排好的任务,按照顺序写进主线程里,等线程执行时,这些任务就是 按照顺序在线程中依次被执行;等所有任务执行完成之后,线程会自动退出。
- 处理线程中产生的新任务,运用事件循环机制。
引入循环机制,在线程语句后面添加了一个 for 循环语句,让线程会一直循环执行。 引入事件,在线程运行过程中,等待用户的操作事件,等待过程中线程处于暂停状态,等接收到用户的操作之后再激活线程,然后继续执行。 - 处理其他线程发送过来的任务,运用消息队列。
消息队列:一种数据结构,存放要执行的任务,队列先进先出
- 在线程之间添加一个消息队列
- 其他线程产生新任务添加进消息队列尾部
- 主线程循环地从消息队列头部读取任务
- 处理其他进程发送过来的任务
渲染进程专门有一个 IO 线程 用来接收其他进程传进来的消息,接收到消息之后,会将这些消息组装成任务发送给渲染主线程。 - 处理高优先级的任务,运用微任务。
宏任务 macrotask / jobs:消息队列中的任务称为宏任务,每个宏任务中都包含了一个微任务队列,在执行宏任务的过程中,如果 DOM 有变化,那么就会将该变化添加到微任务列表中,这样就不会影响到宏任务的继续执行,因此也就解决了 执行效率 的问题。 微任务 microtask / task:等宏任务中的主要功能都直接完成之后,这时候,渲染引擎并不着急去执行下一个宏任务,而是 执行当前宏任务中的微任务,因为 DOM 变化的事件都保存在这些微任务队列中,这样也就解决了 实时性问题。微任务和宏任务是绑定的,每个宏任务在执行时,会创建自己的微任务队列 - 单个任务执行时间过长,运用回调功能
因为所有的任务都是在单线程中执行的,所以 每次只能执行一个任务,而其他任务就都处于等待状态。如果其中一个任务执行时间过久,那么下一个任务就要等待很长时间。 针对这种情况,JavaScript 是通过 回调功能 来规避这种问题的,也就是让要执行的 JavaScript 任务滞后执行。
- 微任务包括
process.nextTick ,promise ,MutationObserver ,其中 process.nextTick 为 Node.js 独有。 - 宏任务包括
<script> ,setTimeout ,setInterval ,setImmediate ,I/O ,UI rendering 。
Event Loop 的执行顺序如下所示:
- 首先执行执行栈中的 同步代码,这属于宏任务
- 当执行完所有同步代码后,执行栈为空,查询是否有 异步代码 需要执行
- 之后执行所有 微任务
- 当执行完所有微任务后,如有必要会渲染页面
- 然后开始下一轮 Event Loop ,执行宏任务中的异步代码,也就是 setTimeout 中的回调函数
这里很多人会有个误区,认为微任务快于宏任务,其实是错误的。因为宏任务中包括了 script,浏览器会先执行一个宏任务,接下来有异步代码的话才会先执行微任务。
这一次,彻底弄懂 JavaScript 执行机制
回调
不同的回调执行时机:宏任务和微任务 Event Loop :你知道它们的打印顺序吗? 回调函数:该函数作为一个参数传递给另一个函数 同步回调:在主函数返回之前执行回调函数 异步回调:在主函数外部执行回调函数
- 第一种就是把异步函数做成一个任务(宏任务),添加到 消息队列 (延迟执行队列或普通的消息队列)的尾部,之后从消息队列取出维护一个新的调用栈去执行。比如 setTimeout 和 XMLHttpRequest 的回调函数。
- 第二种就是把 异步函数添加到微任务队列 中,这样就是在当前任务(当前调用栈)的末尾处(在主函数执行结束之后、当前宏任务结束之前)执行了。比如 promise,MutationObserver 的回调函数。
function call() {
console.log('call');
}
function test(callback) {
console.log('start');
callback();
console.log('end');
}
test(call);
function call() {
console.log('call');
}
function test(callback) {
console.log('start');
setTimeout(callback, 0);
console.log('end');
}
test(call);
(原文有很多例子)
- promise 比 setTimout 早输出
- Promise 一旦被定义就会立即执行
- promise 的 .then 放入等待队列
- await … ; 这里的 … 执行完就跳出 async ,把await 后面 async 中的语句加到等待队列
|