本文主要参阅了如下两篇文章,对JS的Event Loop运行机制基础知识进行了整理。
从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理
JavaScript 运行机制详解:再谈Event Loophtml
你们都知道JavaScript是单线程的,这就引伸出一个问题,进程与线程是什么,他们的区别是什么?
先给出进程和线程的定义:前端
用工厂和工人的例子来形象阐述:ajax
- 进程是一个工厂,工厂有它的独立资源 -> 系统分配的内存(独立的一块内存) - 工厂之间相互独立 -> 进程之间相互独立 - 线程是工厂中的工人,多个工人协做完成任务 -> 多个线程在进程中协做完成任务 - 工厂内有一个或多个工人 -> 一个进程由一个或多个线程组成 - 工人之间共享工厂的资源 -> 同一进程下的各个线程之间共享进程的内存空间(包括代码段、数据集、堆等)
补充:segmentfault
关于浏览器进程问题能够简单基础三点:浏览器
平时 coding 接触到最多的一个浏览器进程是浏览器渲染进程(浏览器内核),它管理着页面渲染。脚本执行,事件处理等。要同时处理这么多事情,渲染进程显然是多线程的,它主要包括如下5个常驻线程:多线程
setInterval
和setTimeout
就归这个线程管理。ajax
发出http请求后,接收响应、检测状态变动等都是这个线程管理的。咱们常说的JavaScript是单线程的,其实就是说的JS引擎是单线程的,它仅仅是浏览器渲染进程种的一个线程。为何呢?由于JavaScript的主要做用是与用户互动,以及操做DOM
,若是JavaScript有两个线程,一个线程对一个DOM
节点执行 A 操做,另外一个线程这个DOM
节点执行 B 操做,那么就会起冲突,因此JavaScript在前端的应用就注定了它是单线程的。异步
然而JavaScript的单线程特性就注定咱们不用它去完成密集的 cpu 运算,由于密集 cpu 运算耗时过长,阻塞页面渲染。为了解决这个问题,HTML5提出 Web Worker 标准,容许JavaScript脚本建立多个线程,可是子线程彻底受主线程控制,且不得操做DOM
。函数
JavaScript 是单线程的带来的问题是:全部任务都必须同步执行,问题就出现了,不少 I/O 过程是很是耗时的(如http 请求数据),若是要等到 I/O 过程结束再执行后续任务,就会出现页面的卡顿、cpu 的闲置。因而异步的任务就出现了,异步任务是指挂起处于等待中的任务,继续执行同步任务,等到结果返回再去继续执行被挂起的任务。因而,JavaScript 的任务能够分为同步任务和异步任务。下面就引出 Event Loop 机制:oop
如上图所示,执行栈中的代码会调用一个异步的API,它们会在任务队列中添加各类事件(或者说回调函数),另外用户的操做如click
、mousedown
等都会在任务队列中添加事件。只要执行栈中的代码执行完毕,主线程就会去读取任务队列,将可执行的回调函数放到执行栈中执行。布局
总结一下:
执行栈执行完毕 -> 主线程读取任务队列,并执行回调函数 -> 执行栈执行完毕 -> 主线程读取任务队列,并执行回调函数 ...
这个过程一直循环下去,因此就叫事件循环(Event Loop)。
前面提到了浏览器的定时触发器线程,它的主要做用就是计时,setTimeout
和 setInterval
就由它来控制,原理就是到达设置时间后,往任务队列中添加这两个函数中指定的回调函数。
setTimeout()
方法用于在指定的毫秒数后调用函数或计算表达式。可是须要注意的是,实际是计时结束后定时触发器线程才会将回调函数放到任务队列中去,此时任务队列中这个回调以前可能已经有一些事件待处理,而且必定要执行栈的任务执行完后才会开始执行任务队列中的任务,因此 setTimeout()
中回调开始执行的时间是:执行栈执行时间 + 任务队列前方回调执行时间 + 延迟时间
setInterval()
方法可按照指定的时间间隔来周期性调用函数或计算表达式。它的问题在于:每次都精确的隔一段时间将一个回调放到任务队列中,并无考虑到内部回调函数执行所需时间,这就会致使两种问题: