博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
详解 JavaScript 中的 EventLoop(事件循环)机制
阅读量:6794 次
发布时间:2019-06-26

本文共 2035 字,大约阅读时间需要 6 分钟。

前言

  我们都知道,javascript 从诞生之日起就是一门单线程的非阻塞的脚步语言。这是由其最初的用途来决定的:与浏览器交互 。

  单线程意味着,JavaScript 代码在执行的时候,都只有一个主线程来处理所有的任务。

  而非阻塞则是当代码需要进行一项异步任务(无法立刻返回结果,需要花一定的事件才能返回的任务,如 I/O 事件)的时候,主线程会挂起这个任务,然后在异步任务返回结果的时候再根据一定的规则去执行相应的回调。

  单线程是必要的,也是 JavaScript 这门语言的基石,原因之一在其最初也是主要的执行环境---浏览器中,我们需要进行各种各样的 dom 操作。试想一下,如果 JavaScript 是多线程的,那么当两个线程同时对 dom 进行一项操作,例如一个向其添加事件,而另一个删除了这个 dom ,此时该如何处理呢?因此,为了保证不会发生类似的情景,JavaScript 选择只用一个主线程来执行代码,这样就保证了程序执行的一致性。

  当然,现如今人们也意识到,单线程在保证了执行顺序的同时也限制了 JavaScript 的效率,因此开发出了 web worker 技术。这项技术号称让 JavaScript 成为一门多线程语言。然而,使用 web worder 技术开的多线程有着诸多的限制,例如:所有新线程都受主线程的完全控制,不能独立执行。这意味着这些“线程”实际上应属于主线程的子线程。另外,这些子线程并没有执行 I/O 操作的权限,只能为主线程分担一些诸如计算等任务。所以严格来讲这些线程并没有完整的功能,也因此这项技术并非改变了 JavaScript 语言的单线程本质。

浏览器环境下 js 的事件循环机制

1. 执行栈于事件队列

  当 JavaScript 代码执行的时候会将不同的变量存于内存中的不同位置: 堆(heap)和 栈(stack)中加以区分。其中,堆中存放这对象。而栈中这存放着一些基础类型变量以及对象的指针。但我们这里说的执行栈和上面的意义却有些不同。

  我们知道当我们调用一个方法的时候,js 会生成一个于这个方法对应的执行环境(context),又叫执行上下文。这个执行环境中存在着这个方法的私有作用域,上层作用域的指向,方法的参数,这个作用域中定义的变量以及这个作用域的 this 对象。而当一系列的方法被依次调用调用的时候,因为 js 是单线程的,同一时间只能执行一个方法,于是这些方法被排队在一个单独的地方。这个地方被称为执行栈。

  当一个脚步第一次执行的时候,js 引擎会解析这段代码,并将其中的同步代码安装执行顺序加入执行栈中,然后从头开始执行。如果当前执行的是一个方法,那么 js 会向执行栈添加这个方法的执行环境,然后进入这个执行环境继续执行其中的代码。当这个执行环境中的代码执行完毕并返回结果后,js 会退出这个执行环境并把这个执行环境销毁,回到上一个方法的执行环境中!!这个过程反复进行,直到执行栈中的代码全部执行完毕。

  下面这个图片非常直观的展示了这个过程,其中的 global 就是初次运行脚步时向执行栈中加入的代码:

  从图片中可以看出,一个方法执行会向执行栈中加入这个方法的执行环境,在这个执行环境中还可以调用其他方法,甚至是自己,其结果不过是在执行栈中再添加一个执行环境,除非发生了栈溢出,及超出了所使用内存的最大值。

  以上的过程说的都是同步代码的执行。那么当一个异步代码(入发送 ajax 请求数据)执行后会如何呢?前文提到,js 的另一大特点是非阻塞,实现这一点的关键在于下面要说的这项机制 -- 事件队列(Task Queue)

  js引擎遇到一个异步事件后并不会一直等待器返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务。当一个异步事件返回结果后,js 会将这个事件加入与当前执行栈不同的另一个队列,我们称之为事件队列。被放入事件队列不会执行其回调,而是等待执行栈中的所有任务都执行完毕,主线程处于闲置状态时,主线程会去查找事件队列中是否有任务。如果有,那么这个事件对应的回调放入执行栈中,然后执行其中的同步代码...,如此反复,这样就形成了一个无限的循环。这就是这个过程被称为“事件循环(Event Loop)”的原因。

这里还有一张图来展示这个过程:

图中的 stack 表示我们所说的执行栈,web apis 则是代表一下异步事件,而 callback queue 即事件队列

 

2. macro tack 与 micro task

  以上的事件循环过程是一个宏观的表述,实际上因为异步任务之间并不相同,因此他们的执行优先级也有区别。不同的异步任务被分为两类:微任务(micro task)和宏任务(macro task)。

  

 

转载于:https://www.cnblogs.com/yymxp/p/9037611.html

你可能感兴趣的文章
json,serialize,msgpack比较
查看>>
javaweb异常提示信息统一处理(使用springmvc,附源码)
查看>>
Java同步块
查看>>
关于java字节码框架ASM的学习
查看>>
深入浅出: Java回调机制(异步)
查看>>
Fork/Join框架(六)取消任务
查看>>
高可用Hadoop平台-HBase集群搭建
查看>>
iOS开发之网络编程--使用NSURLConnection实现大文件下载
查看>>
linux安装AWStats业务数据分析工具
查看>>
【SICP练习】12 练习1.18
查看>>
Error establishing a database connection 的解决方法(发现黑客入侵)
查看>>
Vuex概念浓缩版记录
查看>>
数组对象去重
查看>>
React-Native 学习笔记(一)
查看>>
电子商务 javaweb b2b b2c o2o平台
查看>>
[swift 进阶]读书笔记-第十章:协议 C10P1 面向协议编程 Overload Resolution for Free Functions...
查看>>
windows计算程序运行时间
查看>>
vSphere 5.5:使用 RVC VSAN 监控工具第 1 部分
查看>>
我国.ORG域名16.4万居全球第6:1月第三周增450个
查看>>
JS中class的实现方式,另模拟dojo.declare
查看>>