欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  web前端

详解JavaScript运行机制以及概念分析

程序员文章站 2022-03-15 20:04:51
...

对JavaScript有个很模糊的印象,它是单线程异步的。本文主要来说说JavaScript到底是怎么运行的。但在这之前,让我们先理一下这些概念(现学现卖)

基本概念

线程与进程

进程(Process)是系统资源分配和调度的单元。一个运行着的程序就对应了一个进程。一个进程包括了运行中的程序和程序所使用到的内存和系统资源。如果是单核CPU的话,在同一时间内,有且只有一个进程在运行。但是,单核CPU也能实现多任务同时运行,比如你边听网易云音乐的每日推荐歌曲,边在网易有道云笔记上写博文。这算开了两个进程(多进程),那运行的机制就是一会儿播放一下歌,一会儿响应一下你的打字,但由于CPU切换的速度很快,你根本感觉不到,以至于你认为这两个进程是在同时运行的。进程之间是资源隔离的。

那线程(Thread)是什么?线程是进程下的执行者,一个进程至少会开启一个线程(主线程),也可以开启多个线程。比如网易云音乐一边播放音频,一边显示歌词。多进程的运行其实也就是通过进程中的线程来执行的。一个进程下的线程是共享资源的。当多个线程同时操作同一个资源的时候,就出现资源争抢的问题。这又是另外一个问题了。

并行与并发

并行(Parallelism)是指程序的运行状态,在同一个时间内有几件事情并行在处理。由于一个线程在同一时间只能处理一件事情,所以并行需要多个线程在同一时间执行多件事情。

而并发(Concurrency)是指程序的设计结构,在同一时间内多件事情能被交替地处理。重点是,在某个时间内只有一件事情在执行。比如单核CPU能实现多任务运行的过程就是并发的。

同步与异步

同步异步是指程序的行为。同步(Synchronous)是程序发出调用的时候,一直等待直到返回结果,没有结果之前不会返回。也就是说,同步是调用者主动等待调用过程。

异步(Asynchronous)是发出调用之后,马上返回,但是不会马上返回结果。调用者不必主动等待,当被调用者得到结果之后会主动通知调用者。

举个例子,去奶茶店买饮料。同步就是,一个顾客说出需求(请求),然后一直等着服务员做好饮料,顾客拿到自己点的饮料之后才离开;然后下一个顾客继续重复上述过程。异步就是,顾客先排队点单,点完之后拿着单子在一边,等服务员做好之后会叫号,叫到你了你去拿就好。

所以线程跟同步异步没有直接的关系,单线程也是可以实现异步的。至于实现的方式,下面会具体说到。

阻塞与非阻塞

阻塞与非阻塞是指等待状态。阻塞(Blocking)是指调用在等待的过程中线程被“挂起”(CPU资源被分配到其他地方去了)。

非阻塞(Non-blocking)是指等待的过程CPU资源还在该线程中,线程还能做其他的事情。

以刚才排队买饮料的例子,阻塞就是你在等待的时候什么事情也做不了,而非阻塞是你在等待的时候可以管自己先做其他的事情。

所以,同步可以阻塞也可以非阻塞,异步可以阻塞也可以非阻塞。

单线程的JS

大概理清楚上述概念之后呢,就知道单线程和异步是没有矛盾的。那JS是如何执行的呢?JS其实就是一门语言,说是单线程还是多线程得结合具体运行环境。JS的运行通常是在浏览器中进行的,具体由JS引擎去解析和运行。下面我们来具体了解一下浏览器。

浏览器

目前最为流行的浏览器为:Chrome,IE,Safari,FireFox,Opera。浏览器的内核是多线程的。一个浏览器通常由以下几个常驻的线程:

  • 渲染引擎线程:顾名思义,该线程负责页面的渲染

  • JS引擎线程:负责JS的解析和执行

  • 定时触发器线程:处理定时事件,比如setTimeout, setInterval

  • 事件触发线程:处理DOM事件

  • 异步http请求线程:处理http请求

需要注意的是,渲染线程和JS引擎线程是不能同时进行的。渲染线程在执行任务的时候,JS引擎线程会被挂起。因为JS可以操作DOM,若在渲染中JS处理了DOM,浏览器可能就不知所措了。

JS引擎

通常讲到浏览器的时候,我们会说到两个引擎:渲染引擎和JS引擎。渲染引擎就是如何渲染页面,Chrome/Safari/Opera用的是Webkit引擎,IE用的是Trident引擎,FireFox用的是Gecko引擎。不同的引擎对同一个样式的实现不一致,就导致了经常被人诟病的浏览器样式兼容性问题。这里我们不做具体讨论。

JS引擎可以说是JS虚拟机,负责JS代码的解析和执行。通常包括以下几个步骤:

  • 词法分析:将源代码分解为有意义的分词

  • 语法分析:用语法分析器将分词解析成语法树

  • 代码生成:生成机器能运行的代码

  • 代码执行

不同浏览器的JS引擎也各不相同,Chrome用的是V8,FireFox用的是SpiderMonkey,Safari用的是JavaScriptCore,IE用的是Chakra。

回到JS是单线程这句话,本质上来说,是浏览器在运行时只开启了一个JS引擎线程来解析和执行JS。那为什么只有一个引擎呢?如果同时有两个线程去操作DOM,浏览器是不是又要不知所措了?!

JS运行机制

说了这么多之后,终于我们要说到JS的整个运行过程了。

同步执行

我们先看下JS同步执行过程是怎么做到的?这就涉及到了一个很重要的概念——执行上下文。我的一篇译文深入学习JavaScript闭包很详细地说明了这个概念。

执行上下文记录了代码运行时的环境,当前运行状态下有且有一个执行上下文在起作用。那执行上下文到底记录了什么呢?大概有词法环境,变量环境等。举个简单的例子:

var x = 10;

function foo(){
    var y=20;
    function bar(){
        var z=15; 
    }
    bar();
}

foo();

代码运行,首先进入全局上下文。然后执行foo()的时候,就进入了foo上下文,当然此时全局上下文还在。当执行 bar()的时候,又进入了bar上下文。执行完毕bar(),回到foo上下文。执行完foo(),又回到全局上下文。所以,执行过程执行上下文会形成一个调用栈(Call stack),先进后出。

// 进栈
                                                          3 bar Context
    =>                      =>   2 foo Context     =>     2 foo Context
        1 global Context         1 global Context         1 global Context

// 出栈
3 bar Context
2 foo Context     =>   2 foo Context     =>                        =>
1 global Context       1 global Context         1 global Context

在JS执行过程中,有且仅有一个执行上下文在起作用。因为JS是单线程的,一次只能做一件事。

以上的过程都是同步执行的。

异步执行——事件循环

我们回顾一下JS中自带了哪些原生的异步事件:

  • setTimeout

  • setInterval

  • 事件监听

  • Ajax请求

  • etc..

JS异步的效果得益于浏览器的执行环境。实际上,浏览器又开了线程来处理这些BOM事件。举例:

function foo(){
    console.log(1);
}

function bar(){
    console.log(2);
}

foo();

setTimeout(function cb(){
    console.log(3);
});

bar();

按照上一节的分析,首先进入全局上下文,运行至foo(),进入了foo上下文环境;执行console.log(1),控制台输出1;foo上下文环境出栈,运行至setTimeout,交给浏览器的定时处理线程;运行至bar(),进入了bar上下文环境;执行console.log(2),控制台输出2;foo上下文环境出栈;等到浏览器线程执行完setTimeout,返回cb()回调函数至当前任务队列;当发现执行栈为空时,浏览器的JS引擎会执行一次循环,将事件队列的队首出队至JS执行栈中;执行cb(),进入cb上下文环境;执行console.log(3),控制台输出3;事件队列为空,全局上下文出栈。

以上就是JS引擎的事件循环机制,是实现异步的一种机制。主要涉及到浏览器线程任务队列以及JS引擎。所以,我们可以看出,JS的异步请求,借助了而它所在的运行环境浏览器来处理并且返回结果。而且,这也解释了为什么那些回调函数的this指向window,因为这些异步的代码都是在全局上下文环境下执行的。

后话:不知道自己理解对了没有,也不知道说得清楚不清楚。不当之处,欢迎指出。

参考资料:

  • JavaScript:彻底理解同步、异步和事件循环(Event Loop)

  • 还在疑惑并发和并行?

  • IMWeb社区 浏览器进程?线程?傻傻分不清楚!

  • Javascript是单线程的深入分析

  • JavaScript单线程和浏览器事件循环简述

  • AlloyTeam 【转向Javascript系列】从setTimeout说事件循环模型

  • 小胡子哥 JavaScript异步编程原理

  • 阮一峰 JavaScript 运行机制详解:再谈Event Loop

  • 【朴灵评注】JavaScript 运行机制详解:再谈Event Loop

  • Philip Roberts: Help, I’m stuck in an event-loop.

  • Philip Roberts: What the heck is the event loop anyway?

  • jquery中Ajax的异步和同步

以上就是详解JavaScript运行机制以及概念分析的详细内容,更多请关注其它相关文章!