Event Loop详解

1.进程,单线程与多线
  • 进程: 运行的程序就是一个进程,好比你正在运行的浏览器,它会有一个进程。
  • 线程: 程序中独立运行的代码段。
  • 一个进程由单个或多个线程组成,线程是负责执行代码的。
2.单线程与多线程的区别
  • 单线程 从头执行到尾,一行一行执行,若是其中一行代码报错,那么剩下代码将再也不执行。同时容易代码阻塞。
  • 多线程 代码运行的环境不一样,各线程独立,互不影响,避免阻塞。
任务队列

任务分两种:node

一种是同步任务(synchronous),另外一种是异步任务(asynchronous)。ajax

  • 同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
  • 异步任务指的是,不进入主线程、而进入"任务队列"(taskqueue)的任务,只有"任务队列"通知主线程,某个异步任务能够执行了,该任务才会进入主线程执行

"任务队列"是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空,"任务队列"上第一位的事件就自动进入主线程。可是,因为存在后文提到的"定时器"功能,主线程首先要检查一下执行时间,某些事件只有到了规定的时间,才能返回主线程数组

Js 中,有两类任务队列:宏任务队列(macro tasks)和微任务队列(micro tasks)。宏任务队列能够有多个,微任务队列只有一个。那么什么任务,会分到哪一个队列呢?浏览器

  • 宏任务:script(全局任务), setTimeout, setInterval, setImmediate, I/O, UI rendering.
  • 微任务:process.nextTick, Promise, Object.observer, MutationObserver.
    咱们上面讲到,当stack空的时候,就会从任务队列中,取任务来执行。共分3步:数据结构

  • 取一个宏任务来执行。执行完毕后,下一步。
  • 取一个微任务来执行,执行完毕后,再取一个微任务来执行。直到微任务队列为空,执行下一步。
  • 更新UI渲染。多线程

Event Loop 会无限循环执行上面3步,这就是Event Loop的主要控制逻辑。其中,第3步(更新UI渲染)会根据浏览器的逻辑,决定要不要立刻执行更新。毕竟更新UI成本大,因此,通常都会比较长的时间间隔,执行一次更新。异步

3. Event Loop(浏览器)

js既然是单线程,那么确定是排队执行代码,那么怎么去排这个队,就是Event Loop。虽然JS是单线程,但浏览器不是单线程。浏览器中分为如下几个线程:async

  • js线程
  • UI线程
  • 事件线程(onclick,onchange,...)
  • 定时器线程(setTimeout, setInterval)
  • 异步http线程(ajax)

其中JS线程和UI线程相互互斥,也就是说,当UI线程在渲染的时候,JS线程会挂起,等待UI线程完成,再执行JS线程.函数

主线程从"任务队列"中读取事件,这个过程是循环不断的,因此整个的这种运行机制又称为Event Loop(事件循环)。oop

  1. 全部同步任务都在主线程上执行,造成一个执行栈(execution context stack)。
  2. 主线程以外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
  3. 一旦"执行栈"中的全部同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,因而结束等待状态,进入执行栈,开始执行。
  4. 主线程不断重复上面的第三步。

在进程启动时,node便会建立一个相似while(true)的循环,每执行一次循环体的过程咱们称为tick,每一个tick的过程就是查看是不是否有事件等待处理,若是有,就取出事件及其相关的回调函数。若是有关联的回调函数,就执行他们,而后进入有下一个循环体

4.异步的事件

process.nextTick()
  • 性能上来讲很轻量,时间复杂的O(1)
  • 回调函数保存在数组中,在一轮循环中数组中的回调函数所有执行完

    setImmediate()
  • 和上面同样都是将回调函数延迟执行,不一样上面的优先级别要高,缘由是事件循环对观察者的检查是有前后顺序的,process.nextTick()属于idle观察者,setImmediat属于check观察者。在每一轮循环中,idle先与I/O观察者,I/观察者先于check观察者。
  • 回调函数存在链表中,每一次循环只执行链表中的一个回调函数
    时间复杂度lg(n)

    定时器setTimeout()/setInterval()
  • 调用时会建立定时器会被插入到定时器观察者内部的一个红黑树中,每次tick时,会取出定时器,检查是否超时,若是超时,就造成一个事件,执行回调函数。问题就是时间不必定精确
  • 时间复杂度lg(n)

相关文章
相关标签/搜索