• js事件循环机制辨析


     对于新接触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
  • 相关阅读:
    s2sh的MVC执行流程和执行原理
    码支付-个人支付接口-个人收款
    notepad++ FTP同步插件【FanFtpTools】2.0
    自己做的notepad++ FTP同步插件【FanFtpTools】
    thinkphp高并发抢购代码测试-解决高并发下的超卖问题!
    thinkphp5.0 与thinkphp5.1 db()函数的区别
    thinkphp5.1 长连接-单例模式测试之二
    thinkphp5.1长连接-单例模式测试!
    php 全文搜索引擎-讯搜使用
    在线WEB开发编辑器,edt.df5d.com
  • 原文地址:https://www.cnblogs.com/caiyy/p/10362247.html
Copyright © 2020-2023  润新知