乐趣区

关于前端:究竟何为event-loop为什么总是把它和异步任务一起谈

家喻户晓,JavaScript 是单线程的,然而当初也常常须要 JavaScript 进行一些异步的工作,这时候就会用到 setTimeoutasync awaitpromise 等 api。可能你就要问了,那么 JavaScript 到底是怎么实现异步工作的呢?JavaScript 不是单线程嘛? 而且这篇文章不是在说事件循环嘛,这和 JS 实现异步工作又有什么关系?

先别急,咱们先深究一下,到底什么是”单线程“?常常和”线程“等量齐观的”过程“又是什么?

过程和线程

官网概念:

  • 过程(process):计算机运行的程序
  • 线程(thread):操作系统能运行运算调度的最小单位,是过程中的理论运作单位

能够简略了解为每当咱们在计算机外面运行的一个软件程序,就会开启一个新过程来运行该程序(有时候也会是多个过程,浏览器就是多过程)。其中一个过程外面能够领有多个线程,并且过程中能够并发 多个 线程,让每条线程别离执行不同的工作。而每个线程内是依照 繁多程序 来执行代码的。

单线程的 JavaScript

所以后面说的:JavaScript 是单线程 ,就意味着 JavaScript 只能依照程序执行代码,如果后面的代码执行耗时长,前面的代码只能期待,导致 梗塞 景象。设想一下后面的代码发了个申请要等到几十秒 …….. 前面的全副代码都要等着 …..

这时候你可能又想问了:为什么 JavaScript 要设置为单线程啊?一开始就设置为多线程不香吗?

这和 JavaScript 的用处无关,它作为浏览器脚本语言,有一个外围的用处是:与用户互动以及操作 DOM。设想一下,如果 JavaScript 是多线程,我在一个线程上对某个 DOM 节点增加货色,又在另一个线程把这个 DOM 删除了,那到底是先删除还是先增加呢?为了防止这样的简单状况,JavaScript 只能设置为单线程。

然而为了防止后面提及的梗塞景象,又须要异步工作。

那么又回到最结尾的问题:那么 JavaScript 到底是怎么实现异步工作的呢?

提供异步能力的 runtime

JavaScript 的异步能力其实是由运行环境 (JavaScript runtime) 所提供的。举个例子,JavaScript 能够在 chrome 中执行,也能够在 node 中执行,那么 chromenode都是JavaScript Runtime

如图是一个浏览器 JavaScript runtimenodejs 有所不同)的繁难图,让咱们简略看看这个构造:

能够把 JavaScript runtime 了解成一个过程区域 (线程池),次要包含 JS 引擎(JavaScript Engine) 和Web API、回调队列 (图中的task queuemicrotask queue),它们三者的次要作用如下(具体能够参考:https://juejin.cn/post/6844903908452597768):

  • JS 引擎:

    • 内存堆 (memory heap) 调配内容来存储援用类型的理论值等
    • 调用栈 (call stack) 将 JS 源码解析、转行成一个个可执行单元(后面提到的 JS 代码是单线程就是体现在该调用栈中:所有 JS 代码都在该 惟一 的调用栈依照程序执行)
  • Web API:后面提到的为 JS 自身提供异步能力的个性汇合就是浏览器的 Web API,其实 setTimeoutsetInternal 等异步 api 并不是 js 本身带有的,是浏览器提供的
  • 回调队列:该模块帮助 Web API 解决异步操作,用于保留曾经叫用的callback

除了在这三个模块外,还有一个事件循环(Event loop),它就是这次的配角,它负责 JS 实现异步的机制和“检察官”

说到这里,咱们其实还是不晓得 javascript runtime 中的 event loop 是怎么为咱们提供异步能力的,这下咱们先对浏览器 event loop 进行深挖了~(node 中环境有所不同)

浏览器的事件循环

先来看看 event loop,它是javascript runtime 的调节机制,它的次要作用是:

The job of the Event loop is to look into the call stack and determine if the call stack is empty or not(查看调用栈并且判断它目前状态是否为空). If the call stack is empty, it looks into the message queue to see if there’s any pending callback waiting to be executed(如果调用栈是空的,它又会去看音讯队列 / 回调队列是否有回调在期待执行)

再理解一下 JavaScript 中同步工作和异步工作的执行规定,同步和异步工作别离进入不同的执行 ” 场合 ”:

  1. 要执行的同步工作会被放入调用栈 (call stack) 里排队执行:只有前一个工作执行结束,能力接着执行下一个工作
  2. 异步工作不会进入主线程,而是再有了运行后果后在工作队列 (task queue) 搁置一个事件
  3. 如果调用栈外面所有的同步工作都执行结束,零碎就去工作队列中看看还有哪些异步工作须要执行,将曾经完结的异步工作搁置在工作队列中的事件放进调用栈,开始执行
  4. 上述三步一直地循环,因而变成了“事件循环”

event loop 以及异步工作执行规定联合来看,是不是事件看上去就明了了不少了?但还没完,让咱们再细看一下咱们的工作队列

浏览器的宏工作和微工作

其实工作队列细分为微工作队列 (microtask queue) 和宏工作队列(macrotask queue),不同类型的异步工作事件会别离进入不同的工作队列,而不同的工作队列执行规定也不一样。

宏工作个别包含:ajaxsetTimeoutsetIntervalDOM 监听 UI Rendering

微工作个别包含:Promise 的 then 回调 queueMicrotask()

而两个队列的执行规定如下:

  1. 当调用栈为空,零碎来查看工作队列时,会先看以后的微工作队列状况
  2. 若以后微工作队列里有事件,会将队列里所有的事件都执行完
  3. 再去执行以后宏工作队列外面的一个事件
  4. 执行了一个事件后再查看此时有没有新的微工作事件,有的话要将微工作事件执行完能力执行下一个宏工作

….. 如此循环

说到这儿,你应该也分明 JS 实现异步和事件循环的关系,以及事件循环具体是怎么执行了的吧~ 如有写的不精确的中央欢送提出来

(ps: 其实 nodejs 里的事件循环和浏览器有些不一样,当前有空再写一篇捋一下~)

文章参考:

https://www.youtube.com/watch?v=8aGhZQkoFbQ

https://juejin.cn/post/7083286147920560158

https://www.ruanyifeng.com/blog/2014/10/event-loop.html

https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif

退出移动版