浅析js中的EvntLoop事件循环

  1. 堆(Heap)
  2. 栈(Stack)
  3. 队列(Queue)
  4. 同步任务和异步任务
    1. 异步运行机制
  5. 宏任务和微任务

Evnt Loop即事件循环,是解决JavaScript单线程运行阻塞的一种机制。

堆(Heap)

堆表示一大块非结构化的内存区域。对象、数据被存在堆中

栈(Stack)

栈在JavaScript中又被称为执行栈、调用栈,是一种后进先出的数组结构。
JavaScript有一个主线程(main thread)和调用栈(call-stack或执行栈),主线各所有的任务都会被放到调用栈等待主线程执行。
JS调用栈采用的是后进先出的规则,当函数执行的时候,会被添加到栈的顶部,当执行栈执行完成后,就会从栈顶移除,直到栈内被清空。

这里说的堆栈,是数据结构的堆栈,不是内存中的堆栈(内存中的堆栈,堆存放引用类型的数据,栈存放基本数据类型的数据)

function foo(b){
  const a = 10;
  return a + b + 11
}
function bar(x){
  const y = 3;
  return foo(x * y)
}
console.log(bar(7))
// 42

如上代码,当调用bar时,创建第一个帧,帧中包含了bar的参数和局部变量。当bar调用foo时,第二个帧就被创建出来了,并被压到第一个帧之上,帧中包含了foo的参数和局部变量。当foo返回时,最上层的帧就被弹出栈(剩下bar函数的调用栈)。当bar返回时,栈就空了。

队列(Queue)

队列即任务队列(Task Queue),是一种先进先出的数据结构。在队尾添加新元素,从对头移除元素。

同步任务和异步任务

  • 同步任务是调用立即得到结果的任务,同步任务在主线程上排队执行的任务,只有前一个任务执行完毕后,才能执行后一个任务。
  • 异步任务是调用无法立即得到结果,需要额外的操作才能预期结果的任务,异步任务不进入主线程,而进入“任务队列”的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

js引擎遇到异步任务(DOM事件监听、网络请求、setTimeout计时器等),会交给相应的线程单独去维护异步任务,等待某个时机(计时器结束、网络请求成功、用户点击DOM),然后由事件触发线程将异步对应的回调函数加入到消息队列中,消息队列中的回调函数等待被执行。

异步运行机制

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

主线程从任务队列中读取事件,这个过程是不断循环的,所以整个的这种运行机制又被称为Evnt Loop(事件循环)

事件循环流程图

宏任务和微任务

所有任务分为宏任务(macrotask)微任务(microtask)两种。

  • 宏任务包括script全部代码、setTimeoutsetIntervalsetImmediateI/OUI Rendering
  • 微任务包括Process.nextTickPromiseObject.observe(废弃)、MutationObserver

在挂起任务时,JS引擎会将所有任务按照类别分到两个队列中,首先在宏任务的队列中取出第一个任务,执行完毕后取出微任务队列中的所有任务顺序执行,之后再取宏任务,周而复始,直至两个队列的任务都取完。

宏任务和微任务执行

异步编程的几种方法:

  • 回调函数
  • 事件监听
  • 发布/订阅
  • Promise对象
  • Generator函数
  • async/awai

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 chaoyumail@126.com

×

喜欢就点赞,疼爱就打赏