资讯专栏INFORMATION COLUMN

浅谈 JavaScript 运行机制

URLOS / 2680人阅读

摘要:以多线程的形式,允许单个任务分成不同的部分进行运行。提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源。主线程会不断的重复上诉过程。

众所周知,js是单线程的,说到线程,我们首先来仔细辨析一下线程和进程的知识。
一、进程与线程

阮一峰老师的一篇文章写的很好

cpu会给当前进程分配资源,进程是资源分配的最小单位,进程的资源会分配给线程使用,线程是CPU调度的最小单位

1 ——CPU就像是一个大型的工厂一样。它就像一座工厂,时刻在运行。

2 ——假定工厂的电力有限,一次只能供给一个车间使用。也就是说,一个车间开工的时候,其他车间都必须停工。背后的含义就是,单个CPU一次只能运行一个任务。

3 ——进程好比是一个工厂的车间,它代表CPU所能处理的单个任务,任意时刻,CPU只能运行一个车间的任务,也就是一个进程,其他进程处于等待状态。

4 —— 一个车间里有着许多的工人,他们协同完成一个任务。

5 ——单个工人就好比是一个线程,一个进程可以包括多个线程。

6 —— 车间里的空间是可以供工人们共享的,比如许多房间是每个工人都可以进出的。这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。

7 —— 可是,每间房间的大小不同,有些房间最多只能容纳一个人,比如厕所。里面有人的时候,其他人就不能进去了。这代表一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。

8 —— 一个防止他人进入的简单方法,就是门口加一把锁。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。这就叫"互斥锁"(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域。

9 —— 还有些房间,可以同时容纳n个人,比如厨房。也就是说,如果人数大于n,多出来的人只能在外面等着。这好比某些内存区域,只能供给固定数目的线程使用。

10 —— 这时的解决方法,就是在门口挂n把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。后到的人发现钥匙架空了,就知道必须在门口排队等着了。这种做法叫做"信号量"(Semaphore),用来保证多个线程不会互相冲突。

归纳一下:

以多进程的形式,允许多个任务同时进行。

以多线程的形式,允许单个任务分成不同的部分进行运行。

提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源。

二、JavaScript是单线程

虽然说js是单线程的,但是浏览器并不是单线程的,浏览器中的许多异步行为都是由浏览器去新开一个线程去解决的,js引擎线程是浏览器的线程之一,由于js引擎线程本身是单线程的,所以我们平时说的js单线程指的就是这个了。

浏览器还包括很多其他线程,如界面渲染线程,浏览器事件触发线程,Http请求线程等。

1、证明js是单线程的
// 证明js是单线程的
function foo() {
    console.log("first");
    setTimeout(( function(){
        console.log( "second" );
    }),5);
}
 
for (var i = 0; i < 1000000; i++) {
    foo();
}
// 执行结果会首先全部输出first,然后全部输出second;尽管中间的执行会超过5ms。
2.js为什么要设计成单线程的,
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?

所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

HTML5中增加了web worker可以去创建一个子线程,但是这个线程仍旧是完全受主线程控制,因此,js的单线程性,依旧是没有变化的。

三、任务队列

单线程就意味着,所有的任务队列都需要排队,前一个任务结束,再执行后一个任务,如果前一个任务耗时很长,那么后一个任务就不得不排着。

如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。

js语言的设计者也注意到了这个问题,这时候不管IO,挂起来,去执行等待中的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。

于是这样就出现了同步任务异步任务了。

1、同步任务

同步任务是指主线程排队的任务,只有前一个执行完毕,后一个才能执行。

2、异步任务

不进入主线程,而是进入一个任务队列,只有任务队列,通知了主线程,某一个异步任务才会执行。

下面以AJAX请求为例,来看一下同步和异步的区别:

异步AJAX:

主线程:“你好,AJAX线程。请你帮我发个HTTP请求吧,我把请求地址和参数都给你了。”

AJAX线程:“好的,主线程。我马上去发,但可能要花点儿时间呢,你可以先去忙别的。”

主线程::“谢谢,你拿到响应后告诉我一声啊。”

(接着,主线程做其他事情去了。一顿饭的时间后,它收到了响应到达的通知。)

同步AJAX:

主线程:“你好,AJAX线程。请你帮我发个HTTP请求吧,我把请求地址和参数都给你了。”

AJAX线程:“......”

主线程::“喂,AJAX线程,你怎么不说话?”

AJAX线程:“......”

主线程::“喂!喂喂喂!”

AJAX线程:“......”

(一炷香的时间后)

主线程::“喂!求你说句话吧!”

AJAX线程:“主线程,不好意思,我在工作的时候不能说话。你的请求已经发完了,拿到响应数据了,给你。”

正是由于JavaScript是单线程的,而异步容易实现非阻塞,所以在JavaScript中对于耗时的操作或者时间不确定的操作,使用异步就成了必然的选择。异步是这篇文章关注的重点。

异步过程

所有的同步任务都再主线程上执行,形成一个执行栈

主线程之外还存在一个任务队列,一旦任务队列中的异步任务执行完毕了,就会产生一个事件。

一旦主线程上的同步任务执行完毕了,那么系统就会读取任务队列,看看有哪些事件。那些事件对应的异步任务就结束等待状态,进入执行栈,开始执行。

主线程会不断的重复上诉过程。只要是主线程空了,那么就执行任务队列中的任务。

四、事件和回调函数

"任务队列"是一个事件的队列(也可以理解成消息的队列),IO设备完成一项任务,就在"任务队列"中添加一个事件,表示相关的异步任务可以进入"执行栈"了。主线程读取"任务队列",就是读取里面有哪些事件。

所谓的回调函数就是指被主线程挂起的代码,异步任务必须执行回调函数,当其产生事件,由主线程调入执行栈后就会执行这个回回调函数。

任务队列就是一个先进先出的一个数据结构,排在前面的事件,优先被主线程调用,主线程取得过程上是自动的,只有当主线程一变为空,那么任务队列的第一位就会进入主线程,那么就会执行对应的回调函数。

异步函数
A(args, callbackFn);

一个异步过程包括下面两个要素:

发起函数(或叫注册函数)A

回调函数`callbackFn`

它们都是在主线程上调用的,其中注册函数用来发起异步过程回调函数用来处理结果

来一个例子:

DOM点击事件
var button = document.getElement("#btn");
button.addEventListener("click", function(e) {
    console.log(e);
});

从事件的角度来分析的话,在按钮上添加了一个鼠标点击的监听器,鼠标点击的时候出发。

从异步角度分析:

addEventListener函数就是一个发起函数,第二个回调参数就是回调函数, 事件触发的时候,表示异步任务执行完毕,就产生了事件,将其放入到消息队列中去,等待主线成的调用。

这里又出现了一个新的词汇消息队列,其实这里面放的就是任务队列执行完毕后的那些事件通知。等待着主线程的调用。接下来对其再仔细分析。
五、消息队列和事件循环(Event Loop)
未完待续~~

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/95635.html

相关文章

  • 浅谈小程序运行机制

    摘要:小程序的基础库不会被打包在某个小程序的代码包里边,它会被提前内置在微信客户端。小程序没有重启的概念当小程序进入后台,客户端会维持一段时间的运行状态,超过一定时间后目前是分钟会被微信主动销毁当短时间内连续收到两次 写作背景 接触小程序有一段时间了,总得来说小程序开发门槛比较低,但其中基本的运行机制和原理还是要懂的。比如我在面试的时候问到一个关于小程序的问题,问小程序有window对象吗?...

    Caicloud 评论0 收藏0
  • 浅谈Javascript闭包中作用域及内存泄漏问题

    摘要:将作用域赋值给变量这里的作用域是,而不是将作用域赋值给一个变量闭包返回浏览器中内存泄漏问题大家都知道,闭包会使变量驻留在内存中,这也就导致了内存泄漏。 上一章我们讲了匿名函数和闭包,这次我们来谈谈闭包中作用域this的问题。 大家都知道,this对象是在运行时基于函数的执行环境绑定的,如果this在全局就是[object window],如果在对象内部就是指向这个对象,而闭包却是在运行...

    source 评论0 收藏0
  • 浅谈HTML5 Web Worker

    摘要:事实上,浏览器内部的运行机制是,先将通信内容串行化,然后把串行化后的字符串发给子线程,后者再将它还原。当一个的文档列表中的任何一个对象都是处于完全活动状态的时候,这个会被称之为需要激活线程。 浏览器中的Web Worker 背景介绍 我们都知道JavaScript这个语言在执行的时候是采用单线程进行执行的,也就是说在同一时间只能做一件事,这也和这门语言有很大的关系,采用同步执行的方式进...

    Tecode 评论0 收藏0

发表评论

0条评论

URLOS

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<