对于新接触js语言的人来说,最令人困惑的大概就是事件循环机制了。最开始这也困惑了我好久,花了我几个月时间通过书本,打代码,查阅资料不停地渐进地理解他。接下来我想要和大家分享一下,虽然可能有些许错误的地方,希望大家不吝赐教,感谢感谢。

 这是所涉及的知识点:

  • 观察者模式
  • js的事件循环机制
    • js事件循环机制优缺点及与多线程的比较

观察者模式

 js的事件循环机制是基于观察者模式的,而跟观察者模式相对应的是轮询,我们先来说说轮询的原理。

 我们将轮询映射在现实世界中即为:B不停到A的房间观察房间里是否有人,从而知道A是否回来。

 但显然,这是效率极低的,我们回到代码层面上。B线程使用while(true){观察A的房间,当A在房间内时退出循环}来做到轮询。但是,这样B线程就被堵塞住了,除非退出该循环,否则无法执行接下来的同步代码及异步代码。这对于单线程语言是完全无法接受的,所以我们来看看观察者模式,他是否会堵塞线程。

 同样的,我们来将观察者模式映射到现实世界中:B在自己房间做自己的事情,不再不停地到A的房间看他是否回来,而是当A回到自己房间时,打电话通知B他回来了,B再去房间找A玩。

 该模式最大的优势就是:B可以在等待A回房间的期间,做自己的事情。回到代码层面上,使用观察者模式后,B线程不再被堵塞,A回到房间的信息不再需要B通过循环来同步地监听,而是A用消息传给B线程,B再根据这个消息来执行当A回到房间后应该执行的操作。

 其实当理解了观察者模式的大体流程就已经能够理解js的事件循环机制了。但了解得深入些也没有坏处。接下来我们来用js代码来模拟出一个简易的观察者模式。

代码如下:

var b = {
process_a:mes=>{
console.log('刚刚A发了 %s 的信息,所以我知道A回来了,我该去他房间找他玩了。',mes)
}
} function A(b){
var mes_a = '我是A,我回来了'
b.process_a(mes_a)
} A(b)

 结果如下:

如果大家对同步,异步,堵塞,非堵塞的概念有不理解的地方的话,可以看我的 同步,异步,堵塞,非堵塞,并发 辨析。


事件循环机制

 事件循环机制的核心就是观察者模式。我先给大家描述一遍程序执行的流程。

  1. js程序进入线程,函数入栈,当遇到同步代码的时候就顺序执行,遇到异步代码时,把异步任务抛给WebAPIs执行,然后继续执行接下来的同步代码,直到栈为空。(如若大家对函数栈不了解的话可以看下我的 栈,堆辨析及使用)
  2. 在步骤1进行的同时,WebAPIs执行异步任务,当执行完一个异步任务就将其对应的回调函数放入任务队列(Callback Queue)中等待。
    • WebAPIs是由C++实现的浏览器创建的线程,处理诸如DOM事件,http请求,定时器等异步任务。
  3. 当执行栈为空时,从Callback Queue中取出队列头放入执行栈中,回到第一步。

 给大家一个我画的图,方便理解。

 不过大家可能会疑惑,事件循环机制跟观察者模式哪有什么关系?其实是这样的,在第2步中我写道

当执行完一个异步任务就将其对应的回调函数放入任务队列(Callback Queue)中。

 但我们是如何判断这个异步任务执行完了呢——观察者模式。任务队列是观察者,WebAPIs是被观察者,观察者要求被观察者当发生执行完异步任务这一事件时,通知他执行完了,并将该事件对应的回调函数传过来。

js事件循环机制优缺点及与多线程的比较

 通过事件循环机制,我们就可以实现代码的异步,从而不会堵塞线程。

 通过这一特性,

  1. js在IO上有着卓越的表现,因为IO操作不再会堵塞住线程。
  2. 可以做到高并发。稍微解释一下为什么能够高并发——当同时有多个任务要执行,js将他一一排列起来,然后按顺序执行,这样cpu就不会因为同时要处理的工作太多而负载过大。

 朴灵在《深入浅出nodeJS》中说道:

石器时代:同步。青铜时代:复制线程。白银时代:多线程。黄金时代:事件驱动。

 不过我不敢说事件驱动就是比多线程好,但他确实没有多线程的这些恼人的缺陷。

  1. 如果有大量的线程,会影响性能,因为操作系统需要在线程之间不停进行上下文切换。
  2. 通常数据是多个线程共享的,需要上锁,同时又要防止出现死锁现象。
  3. IO会堵塞住一个线程。

 但同时的,js也有他的缺陷。

  1. 不适合cpu密集型。也解释一下——如一段代码需要非常大量的计算量,以至于他长时间地占着线程,这就堵塞了,后继的同步代码及异步代码都无法执行。不过,html5推出了web worker,可以有效地解决这一缺陷,在本章不表,后面我会专门写一篇文章来讲他。
  2. 只能使用一个线程,无法充分利用计算机的多核cpu。
  3. 可靠性低,一旦一个环节崩溃则整个程序全部崩溃。

 没有一项技术是绝对完美的,但我们要清楚他的优缺点及原因,从而能够充分利用其优点,同时规避其缺点甚至通过自己的方式解决其缺点。


参考资料

  1. Advantages and Disadvantages of a Multithreaded/Multicontexted Application: https://docs.oracle.com/cd/E13203_01/tuxedo/tux71/html/pgthr5.htm
  2. https://www.hostreview.com/blog/160311-the-pros-and-cons-of-using-nodejs: https://www.hostreview.com/blog/160311-the-pros-and-cons-of-using-nodejs
  3. 理解事件循环与任务队列:https://www.jianshu.com/p/e865c3a7ba10

js事件循环机制辨析的更多相关文章

  1. JS 事件循环机制 - 任务队列、web API、JS主线程的相互协同

    一.JS单线程.异步.同步概念 从上一篇说明vue nextTick的文章中,多次出现“事件循环”这个名词,简单说明了事件循环的步骤,以便理解nextTick的运行时机,这篇文章将更为详细的分析下事件 ...

  2. js 事件循环机制 EventLoop

    js 的非阻塞I/O  就是由事件循环机制实现的 众所周知  js是单线程的 也就是上一个任务完成后才能开始新的任务 那js碰到ajxa和定时器.promise这些异步任务怎么办那?这时候就出现了事件 ...

  3. Node.js 事件循环机制

    Node.js 采用事件驱动和异步 I/O 的方式,实现了一个单线程.高并发的 JavaScript 运行时环境,而单线程就意味着同一时间只能做一件事,那么 Node.js 如何通过单线程来实现高并发 ...

  4. js事件循环机制 (Event Loop)

    一.JavaScript是单线程单并发语言 什么是单线程 主程序只有一个线程,即同一时间片断内其只能执行单个任务. 为什么选择单线程? JavaScript的主要用途是与用户互动,以及操作DOM.这决 ...

  5. js事件循环机制(Event Loop)

    javascript从诞生之日起就是一门  单线程的  非阻塞的  脚本语言,单线程意味着,javascript代码在执行的任何时候,都只有一个主线程来处理所有的任务,非阻塞靠的就是 event lo ...

  6. js事件循环机制

    本文参考链接:https://www.jianshu.com/p/cf47bc0bf2ab 一.先搞懂两个东西:堆和栈 栈由操作系统自动分配释放,用于存放函数的参数值.局部变量等一些基本的数据类型,其 ...

  7. JS:事件循环机制、调用栈以及任务队列

    点击查看原文 写在前面 js里的事件循环机制十分有趣.从很多面试题也可以看出来,考察简单的setTimeout也就是考察这个机制的. 在之前,我只是简单地认为由于函数执行很快,setTimeout执行 ...

  8. js的事件循环机制:同步与异步任务(setTimeout,setInterval)宏任务,微任务(Promise,process.nextTick)

    javascript是单线程,一切javascript版的"多线程"都是用单线程模拟出来的,通过事件循环(event loop)实现的异步. javascript事件循环 事件循环 ...

  9. JS JavaScript事件循环机制

    区分进程和线程 进程是cpu资源分配的最小单位(系统会给它分配内存) 不同的进程之间是可以同学的,如管道.FIFO(命名管道).消息队列 一个进程里有单个或多个线程 浏览器是多进程的,因为系统给它的进 ...

随机推荐

  1. bzoj 1064 假面舞会 图论??+dfs

    有两种情况需要考虑 1.链:可以发现对最终的k没有影响 2.环:如果是真环(即1->2->3->4->1),可以看出所有可行解一定是该环的因数 假环呢??(1->2-&g ...

  2. BZOJ_3573_[Hnoi2014]米特运输_树形DP+hash

    BZOJ_3573_[Hnoi2014]米特运输_树形DP+hash 题意: 给你一棵树每个点有一个权值,要求修改最少的权值,使得每个节点的权值等于其儿子的权值和且儿子的权值都相等. 分析: 首先我们 ...

  3. BZOJ_[JSOI2010]Group 部落划分 Group_kruskal

    BZOJ_[JSOI2010]Group 部落划分 Group_kruskal Description 聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒岛上的所有野人都属于同一个部落,野人 ...

  4. Android性能优化-内存泄漏的8个Case

    1为什么要做性能优化? 手机性能越来越好,不用纠结这些细微的性能? Android每一个应用都是运行的独立的Dalivk虚拟机,根据不同的手机分配的可用内存可能只有(32M.64M等),所谓的4GB. ...

  5. python 防止sql注入字符串拼接的正确用法

    在使用pymysql模块时,在使用字符串拼接的注意事项错误用法1 sql='select * from where id="%d" and name="%s" ...

  6. 开源方案搭建可离线的精美矢量切片地图服务-3.Mapbox个性化地图定制入门

    1.简介 mapbox是一家非常牛的公司,比如像特斯拉.DJI大疆创新.孤独星球.Airbnb.GitHub.Cisco.Snap.飞猪.Keep.Bosch这些在国内外各自领域中响当当的企业都是它的 ...

  7. 微服务架构 - 离线部署k8s平台并部署测试实例

    一般在公司部署或者真实环境部署k8s平台,很有可能是内网环境,也即意味着是无法连接互联网的环境,这时就需要离线部署k8s平台.在此整理离线部署k8s的步骤,分享给大家,有什么不足之处,欢迎指正. 1. ...

  8. asp.net core系列 46 Identity介绍

    一. Identity 介绍 ASP.NET Core Identity是一个会员系统,可为ASP.NET Core应用程序添加登录功能.可以使用SQL Server数据库配置身份以存储用户名,密码和 ...

  9. Firemonkey 原生二维码扫描优化

    之前用了ZXing的Delphi版本,运行自带的例子,速度非常慢,与安卓版本的相比查了很多,因此打算使用集成jar的方法,但是总觉得美中不足. 经过一番研究,基本上解决了问题. 主要有两方面的优化: ...

  10. Go:学习笔记兼吐槽(1)

      Go:学习笔记兼吐槽(1) Go:学习笔记兼吐槽(2) Go:学习笔记兼吐槽(3) 自动添加分号 在很多其他的编程语言中,每一行代码的结尾都必须有分号(假设一行中只有一句代码),Golang 的开 ...