javascript知识梳理(三):运行时-事件循环(event loop)

一、前言

本篇文章的目的是尽可能的解释清楚在js中的事件循环,继续通过三个问题着手:

  1. 什么是事件循环机制?
  2. javascript为什么需要事件循环?
  3. 事件循环的优缺点?

二、什么是事件循环?

  • 简单的说,程序在执行代码的过程中,在主线程之外,维护一个事件队列。主线程在执行的过程中,循环的读取事件队列中的任务并执行。这一个整个流程就是事件循环机制。如图:

javascript知识梳理(三):运行时-事件循环(event loop)_第1张图片

  • 事件循环的核心,是通过事件触发的方式,将一个个任务排队放入事件队列,根据队列先进先出的特点。在不影响主线程执行的情况下,将任务按规则排列。既保证了任务的执行顺序,又不阻塞主线程的执行,这种运行机制,非常适合做单线程,多I/O,低运算量的javascript语言执行环境。
  • v8只是个引擎,执行的环境是由浏览器提供的。

三、javascript为什么需要事件循环?

1. javascript是单线程语言

  • javascript最初设计的目标,是作为浏览器的脚本语言,实现与用户的互动,所以它必须要被设计得足够轻量,简单。
  • 而在浏览器中,与用户的互动,即操作网页的呈现方式。既然要改变网页的呈现,则必须改变网页的结构。网页的视图是基于dom树渲染,所以javascript必须要获得操作dom树的能力。
  • 在操作dom树的过程中,有个限制,dom树不能被一个以上的程序同时操作,因为可能会引发未知的错误。比如:
    // a线程执行
    document.body.innerText = '我是a线程'
    // b线程执行
    document.body.innerText = '我是b线程'
    
  • 如上,假如两个线程同时操作dom,页面到底是呈现a线程的执行结果还是b线程的执行结果呢。这很容易引发错误,所以,javascript出现之初,就被设计成单线程,只有单线程才能避免以上问题(既要轻量,又要稳定)。
  • 即使如今javascript引入了开启多线程的功能。但是开启的线程也被禁用了操作domapi。可能就是因为以上原因。

2. 因为单线程所以事件循环

  • 一个线程在执行代码过程中,最影响它的执行效率的,就是阻塞的问题,。
  • 而javascript被设计为网页脚本语言,假如javascript没有事件循环机制,而在与用户的交互过程中,需要应对一下情况:
    1. 调取网络请求,获取数据
    2. 用户点击鼠标或键盘,需展示交互操作
    3. 页面渲染需要执行的一些任务
    4. 内存回收浏览器任务
  1. 执行网络请求:一般网络请求需要一定的时延才能返回结果,而在这段时间内,单线程的javascript必须等待结果返回,才能继续下一步操作。这种情况就容易阻塞线程。对接下来的任务无发实时反馈。
  2. 用户交互:用户在任何时候,都有可能与浏览器进行交互操作。假如操作时,线程正在被阻塞,将会严重影响用户的交互体验。这是不可容忍的。
  • 基于包括但不仅限于以上举例的原因,javascript是需要一个机制来帮忙处理这些不同情况,所以引入了事件循环机制。
  • javascript引入事件循环机制。再遇到以上任务
  1. 执行网络请求:主线程遇到网络请求,将网络请求交给I/O线程(浏览器会为主线程维护I/O线程,处理各种事件),继续执行后续任务。等该网络请求完成,再通过I/O线程将结果包装成任务加入任务队列,等待主线程执行。
  2. 用户交互:用户点击页面,浏览器通知I/O线程,I/O线程也将事件详情包装成任务加入任务队列,等待主线程执行
  • 基于以上原因,javascript引入了事件循环机制。

浏览器事件循环示意图
javascript知识梳理(三):运行时-事件循环(event loop)_第2张图片

三、事件循环的优缺点

1. 优点

  1. 适合处理密集型I/O任务:通过事件循环机制,多个并行的任务,在JavaScript处理起来,只是将不同的任务分配给不同的线程,等待返回结果执行即可。所以处理速度超级快,有很高的实时性。
  2. 适合处理高并发:RESTful Api动辄发起成千上万条请求,但是请求本身并没有太多的计算量,开启多线程处理等待结果又太浪费机器性能。所以事件循环非常适合处理此种场景。
  3. 适合处理少量业务逻辑:例如浏览器中,在处理用户交互事件,页面渲染等少量业务逻辑的场景上,具有很好实时性,能给用户提供很流畅的体验。

2. 缺点

  1. 不适合cpu密集型应用:因为JavaScript单线程的设计,因此,对于高强度运算的任务,可能会因为运算能力有限,导致任务处理时间过长,影响后续任务执行。

解决办法: 将单个cpu密集型任务拆成多个子任务,留出一定时间间隔执行其他任务。

  1. cpu利用率低:因为单线程的原因,cpu多核性能利用率低。

解决办法: 1.开启宿主提供的多线程功能。 2. 如果在node端,通过nginx代理,同时开启多个node进程进行处理。

  1. 安全性低:因为单线程的原因,如果主线程发生错误,将直接导致应用崩溃。

你可能感兴趣的:(javascript,web)