Open lovelmh13 opened 3 years ago
通过事件循环来不断的执行代码。统一调度任务。
JavaScript 是单线程的。意味着每一个任务进来,就会排在下一个任务的后面,直到前面的任务执行完成以后才会执行这个任务。
如何在单线程上添加新的任务,就需要通过事件循环来添加了。不断的循环,如果有新的任务,就被添加到任务队列中来。
不过,这样的模式只限于当前这一个主线程上的新任务添加。
当有其他线程的任务想加入到主线程的时候,需要使用「消息队列」来处理。
IO 线程有新任务进来,添加到 消息队列 的尾部,主线程在循环的时候,从 消息队列 的头部取出任务,加入到主线程去执行。
多个线程都是操作同一个 消息队列,所以需要有一个同步锁。
如输入事件(鼠标滚动、点击、移动)、微任务、文件读写、WebSocket、JavaScript 定时器等等。可以参考Chromium 的官方源码
除此之外,消息队列中还包含了很多与页面相关的事件,如 JavaScript 执行、解析 DOM、样式计算、布局计算、CSS 动画等。
当使用 JavaScript 做一个发布订阅来订阅这些事件(同步),如果非常频繁的话,会使得效率变得低效。比如 DOM 的变化,DOM 经常变化,如果每次变化都通知,那么想想就很低效。
如果使用把他们变成异步的,又会失去时效性。DOM 的变化要求实时性很高。
所以,处理高优先级的任务,我们需要两个指标:高效、实时
消息队列的任务我们称为宏任务,每个宏任务里又有微任务队列。 每次宏任务执行分完成以后,会清空这个宏任务中的微任务队列。
例如,当宏任务执行的时候,DOM 有变化,那么就会把这个任务添加到当前这个宏任务的微任务队列中来。由于不会影响当前宏任务的执行,保证了高效。
等待当前这个宏任务执行完成以后,就去微任务队列里把 DOM 变化的事件解决掉。保证了实时性。
宏任务:由宿主发起的任务。
微任务:由 JavaScript 引擎发起的任务。
由图可以看出,全局上下文里也是有一个微任务队列的。每一个宏任务里,都会有一个微任务队列。当微任务执行的时候,如果有出现了微任务,那么还会添加到当前的微任务队列中来的,不会添加到下一个宏任务的微任务队列中去,所以会优先于下一个宏任务执行。
JavaScript 可以通过回调功能来规避这种问题,也就是让要执行的 JavaScript 任务滞后执行。
如果有一些确定好的任务,可以使用一个单线程来按照顺序处理这些任务,这是第一版线程模型。
要在线程执行过程中接收并处理新的任务,就需要引入循环语句和事件系统,这是第二版线程模型。
如果要接收其他线程发送过来的任务,就需要引入消息队列,这是第三版线程模型。
如果其他进程想要发送任务给页面主线程,那么先通过 IPC 把任务发送给渲染进程的 IO 线程,IO 线程再把任务发送给页面主线程。
消息队列机制并不是太灵活,为了适应效率和实时性,引入了微任务。
浏览器的事件循环
什么是事件循环
通过事件循环来不断的执行代码。统一调度任务。
为什么要有事件循环
JavaScript 是单线程的。意味着每一个任务进来,就会排在下一个任务的后面,直到前面的任务执行完成以后才会执行这个任务。
如何在单线程上添加新的任务,就需要通过事件循环来添加了。不断的循环,如果有新的任务,就被添加到任务队列中来。
不过,这样的模式只限于当前这一个主线程上的新任务添加。
处理其他线程发来的任务
当有其他线程的任务想加入到主线程的时候,需要使用「消息队列」来处理。
IO 线程有新任务进来,添加到 消息队列 的尾部,主线程在循环的时候,从 消息队列 的头部取出任务,加入到主线程去执行。
多个线程都是操作同一个 消息队列,所以需要有一个同步锁。
消息队列里有很多种任务
如输入事件(鼠标滚动、点击、移动)、微任务、文件读写、WebSocket、JavaScript 定时器等等。可以参考Chromium 的官方源码
除此之外,消息队列中还包含了很多与页面相关的事件,如 JavaScript 执行、解析 DOM、样式计算、布局计算、CSS 动画等。
处理高优先级的任务
当使用 JavaScript 做一个发布订阅来订阅这些事件(同步),如果非常频繁的话,会使得效率变得低效。比如 DOM 的变化,DOM 经常变化,如果每次变化都通知,那么想想就很低效。
如果使用把他们变成异步的,又会失去时效性。DOM 的变化要求实时性很高。
所以,处理高优先级的任务,我们需要两个指标:高效、实时
宏任务与微任务
消息队列的任务我们称为宏任务,每个宏任务里又有微任务队列。 每次宏任务执行分完成以后,会清空这个宏任务中的微任务队列。
例如,当宏任务执行的时候,DOM 有变化,那么就会把这个任务添加到当前这个宏任务的微任务队列中来。由于不会影响当前宏任务的执行,保证了高效。
等待当前这个宏任务执行完成以后,就去微任务队列里把 DOM 变化的事件解决掉。保证了实时性。
宏任务:由宿主发起的任务。
微任务:由 JavaScript 引擎发起的任务。
由图可以看出,全局上下文里也是有一个微任务队列的。每一个宏任务里,都会有一个微任务队列。当微任务执行的时候,如果有出现了微任务,那么还会添加到当前的微任务队列中来的,不会添加到下一个宏任务的微任务队列中去,所以会优先于下一个宏任务执行。
解决事件循环中的单个任务执行时间久的问题
JavaScript 可以通过回调功能来规避这种问题,也就是让要执行的 JavaScript 任务滞后执行。
总结
如果有一些确定好的任务,可以使用一个单线程来按照顺序处理这些任务,这是第一版线程模型。
要在线程执行过程中接收并处理新的任务,就需要引入循环语句和事件系统,这是第二版线程模型。
如果要接收其他线程发送过来的任务,就需要引入消息队列,这是第三版线程模型。
如果其他进程想要发送任务给页面主线程,那么先通过 IPC 把任务发送给渲染进程的 IO 线程,IO 线程再把任务发送给页面主线程。
消息队列机制并不是太灵活,为了适应效率和实时性,引入了微任务。