我们都知道JavaScript是一个单个线程,非封锁,异步,解释性脚本语言。单线意味着所有代码均逐步实现,但在实际的Javascript编程中并非如此非块和异步意味着一个任务无需等待执行之前执行先前的任务,这似乎与单线程矛盾。那么为什么JavaScript如此“怪异”?实际上,这与事件循环机制有关。本文探讨了如何从事件周期的角度执行JavaScript。
在阅读本文之前,建议您观看视频,事件循环和循环中是什么,这将使您对事件周期有直观的了解。本文也基于这些视频。
在正式引入事件周期之前,让我们首先了解一些相关的基本知识。
单线程是JavaScript的主要功能,这也意味着只能同时完成一件事。但是为什么不具有Java和其他语言等多个线程来提高执行效率呢?
JavaScript是单线线程与其使用相关的原因。JavaScript的主要用户正在与用户交互和操作DOM。如果设计为多线程,则会带来许多问题。例如,假设有两个线程,一个线程将内容添加到DOM节点,另一个线程删除导致操作冲突的DOM节点。浏览器无法确定哪个操作。
因此,为了避免复杂性,JavaScript是单个线程语言。
哪些过程由浏览器组成,线程包括每个过程及其角色,如下图所示。我的其他文章“浏览器的页面渲染过程”对此进行了更详细的介绍。
JavaScript引擎是执行JavaScript代码的程序或解释器。它主要由两个组成部分组成:
JavaScript是一种单个线程编程语言,这意味着只有一个调用堆栈,因此一次只能完成一件事。当控件流进入函数时,然后将功能放在堆栈的顶部。函数返回末端,该函数从堆栈的顶部弹出。
以下代码:
(2)内存堆对象分配在堆中,堆是用于表示大(通常是非结构化的)内存区域的计算机项。
浏览器提供的API(例如DOM,AJAX,SettieMout等),允许我们同时处理多个任务。完成Web API的内部功能后,将任务转移到任务队列。
在Chrome中,这些API的实现在V8引擎源代码中不存在,因此只能在JavaScript中调用这些API,并且它们的执行不在JS引擎上。例如:Cally settimeout()在正时触发线程。(实际上,这也说明了为什么JavaScript可以执行异步编程)
返回队列,这是一个高级的第一个(FIFO)工作队列,该队列将从Web API接收任务,并通过事件循环进行监视。在呼叫堆栈中执行它。
总而言之,JavaScript的运行时间大致如下:如下:
严格来说,事件周期不是JavaScript本身的机制,而是JavaScript操作环境中的机制(运行时)。定义了HTML标准中的事件周期:
也就是说,事件周期是协调事件,用户交互,脚本,渲染,网络等。
我们知道JavaScript不会独立运行。它的操作取决于主机环境,例如常见的Web浏览器和Node.js.s.,但实际上,技术已经开发到现在,JavaScript也可以嵌入设备和其他设备中。这些设备是JavaScript的主机环境。
这些主机环境的共同点是,有一个称为事件周期的构建机制,该机制将称为JS引擎执行处理程序的多个块的执行。这意味着JS引擎知识的执行环境JS代码由周围环境执行。
那么,事件周期是什么?让我们看一下博客中的解释:
JavaScript的工作方式:事件循环和异步编程的兴起
翻译是:事件周期负责监视和调用堆栈和回调队列。如果堆栈称为空,则事件周期将从队列中删除第一个事件,然后将其推到呼叫堆栈,然后将堆栈调用为执行它。
也许这仍然有些模糊。让我们看一下JavaScript的工作方式中的一部分代码:事件循环和异步编程的兴起:
当我们执行此代码时,它如何在浏览器内部工作:
浏览器的事件周期可能还不清楚,但我看到的最好的解释是:
事件周期负责监视和调用堆栈和恢复队列。如果堆栈被要求为空,则事件周期将从队列中删除第一个事件,然后将其推到呼叫堆栈,然后调用堆栈以执行执行事件的相应回调功能。
由于ES6中介绍的新功能,Promise,Promise的处理程序(处理程序)以及所有异步。为了对异步任务进行适当的管理,还引入了相应的工作概念(作业)和工作队列(工作队列)。ES6中的定义如下:
作业(工作)
作业是一个抽象的封闭,没有参数可以启动ecmascript计算机。
也就是说,操作是一个抽象的封闭,在没有其他ecmascript计算的情况下启动Ecmascript计算。
工作队列(工作队列)
工作队列是pendingjob记录的FIFO队列。每个作业队列都有一个名称,并且全套可用的作业队列由Ecmascript实现定义。
也就是说:工作队列是由pendenjob记录组成的FIFO队列
让我们看一下ES6规范中的描述:
一般含义是:
每个eCmascript实施至少都有以下事件(作业队列):name |目的|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
对于Promise,相应的处理程序是将放置在工作队列中的作业,也就是说,相应的回调函数。
让我们看以下代码:
为什么产生这样的结果?让我们采取以下步骤:
宏任务是我们通常称之为任务,它指的是标准机制计划的任何JavaScript,例如程序的初始化和事件触发器的回调。在使用事件的情况下,您还可以使用或添加任务。
Micropask实际上是上述Ecmascript规范(作业)中定义的工作。Micro -tasks仅来自我们的代码。它们通常是由承诺创建的:处理程序的执行变为Micro -Task.micro -tasks也用于“幕后”,因为它是承诺处理的另一种形式。
任务队列和微征服队列之间的区别很简单,但非常重要:
注意:JavaScript脚本程序的执行也是一项任务。Micro -Tasks必须在执行之前等待执行JavaScript脚本程序。
最后,看看循环中给出的一个有趣的示例。JS代码如下:
当我们运行此代码时,根据Micro -Tasks的操作机制,我们可以快速知道输出:
- > - > - > - >
但是,如果我们单击鼠标的按钮,输出顺序是不同的:
- > - > - > - >
那为什么呢?这是因为当我们单击外部按钮时,执行结束后,堆栈清晰。目前,可以执行微任务,但是如果被调用,则执行结束。尚未清除呼叫堆栈,并且未执行Micro -task。
告诉浏览器 - 您想执行动画,并要求浏览器调用指定的回调函数以更新动画,然后再重新绘制。
这是一个简短的记录,这是三个队列之间的差异。
可能是我不了解浏览器的内部。我在本文中写的不太清楚,而且许多术语并不严格。只是个人研究记录。也欢迎大兄弟提出一些建议。引用了中间和文章中的视频和文章。建议您阅读和观看。
主要参考:
[1]事件循环是什么
[2]在循环中
[3] JavaScript的工作方式:事件循环和异步编程的兴起 + 5种方法与异步/等待/等待更好地编码
原始:https://juejin.cn/post/709779200826932494