JavaScript的运行机制
线程机制
进程与线程
进程(process):程序的一次执行, 它占有一片独有的内存空间,可以通过windows任务管理器查看进程
线程(thread):是进程内的一个独立执行单元,是程序执行的一个完整流程, 是CPU的最小的调度单元
应用程序必须运行在某个进程的某个线程上,一个进程中至少有一个运行的线程: 主线程, 进程启动后自动创建,一个进程中也可以同时运行多个线程, 我们会说程序是多线程运行的
一个进程内的数据可以供其中的多个线程直接共享,多个进程之间的数据是不能直接共享的
线程池(thread pool): 保存多个线程对象的容器, 实现线程对象的反复利用
何为多进程与多线程
多进程运行: 一应用程序可以同时启动多个实例运行
多线程: 在一个进程内, 同时有多个线程运行
比较单线程与多线程
多线程:优点:能有效提升CPU的利用率,创建多线程开销;缺点:线程间切换开销,死锁与状态同步问题
单线程:优点:顺序编程简单易懂;缺点:效率低
JS是单线程还是多线程
js是单线程运行的
但使用H5中的 Web Workers可以多线程运行
浏览器运行是单线程还是多线程
都是多线程运行的
浏览器运行是单进程还是多进程
有的是单进程:FireFox,老版本的IE
有的是多进程:chrome,新版本的IE
如何查看浏览器是否是多进程运行的呢
查看任务管理器-->进程
浏览器内核
支撑浏览器运行的最核心的程序
不同的浏览器可能不一样
Chrome, Safari : webkit
firefox : Gecko
IE : Trident
360,搜狗等国内浏览器: Trident + webkit
内核由很多模块组成
js引擎模块 : 负责js程序的编译与运行(主线程)
html,css文档解析模块 : 负责页面文本的解析(主线程)
DOM/CSS模块 : 负责dom/css在内存中的相关处理 (主线程)
布局和渲染模块 : 负责页面的布局和效果的绘制(内存中的对象)(主线程)
定时器模块 : 负责定时器的管理(分线程)
DOM事件响应模块 : 负责事件的管理(分线程)
网络请求模块 : 负责ajax请求(分线程)
JS代码块
JS中的代码块是指由<script>标签分割的代码段。JS是按照代码块来进行编译和执行的,代码块间相互独立(即就算代码块1出错,但不影响代码块2的加载和执行),但变量和方法共享。
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>无标题文档</title>
<script type="text/javascript">
console.log("这是代码块一");
var a = 100;
function show() {
console.log("show")
}
</script> <script type="text/javascript">
show();
console.log ("这是代码块二");
console.log(a)
</script>
</head>
<body> </body>
</html>
结果可以看到第一个代码块中的方法和变量可以在第二个代码块中访问,但是需要注意的是:需要先定义在使用,因为js是同步并且单线程的,从上往下执行
HTML页面中JS的加载原理
在加载HTML页面的时候,当浏览器遇到内嵌的JS代码时会停止处理页面,先执行JS代码,然后再继续解析和渲染页面。
同样的情况也发生在外链的JS文件中,浏览器必须先花时间下载外链文件中的代码,然后解析并执行它,在这个过程中,页面的渲染和用户互交完全被阻塞。
由于现代浏览器都允许并行下载JS文件,因此<script>标签在下载外部资源时不会阻塞其他的<script>标签。遗憾的是JS下载过程仍然会阻塞其他资源的下载。
JS语言的一大特点就是单线程,也就是说,同一个时间只能做一件事情。之所以是单线程,是因为与它的用途有关,作为浏览器脚本语言,JS的主要用途是与用户互动以及操作DOM。
这决定了它只能是单线程,否则会带来复杂的同步问题。
先来看一段代码然后再来详细的说明js的运行机制,下面的一段代码执行顺序是什么
console.log(1);
setTimeout(function () {
console.log(2);
}, 0);
console.log(3);
console.log(4);
测试得到的结果是1,3,4,2,至于为什么会这么执行,原因就是因为js是单线程的(同一时间只能做一件事情),js的任务队列,同步任务先执行,后执行异步任务(setTimeout就是一个异步任务)
js引擎执行代码的基本流程
先执行初始化代码: 包含一些特别的代码,比如:设置定时器,绑定监听,发送ajax请求。 后面在某个时刻才会执行回调代码
setTimeout(function () {
console.log('timeout 3') //
}, 3000) setTimeout(function () {
console.log('timeout 2') //
alert('2222') //
}, 2000) alert('提示...') // 1, 暂停当前主线程的执行,同时暂停计时,点击确定后恢复主线程执行和计时
console.log('alert之后') //
JS任务可以分为两种:一种是同步任务,另一种是异步任务。注意,只有主线程空了,才会去读取"任务队列",这就是JS的运行机制,这个过程会不断重复。
同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕了,才会执行后一个任务。
异步任务:在主线程之外,还存在一个“任务列队”,异步任务就是不进入主线程,而是进入“任务列队”的任务,只有“任务列队”通知主线程,某个异步任务可以执行了并且同步任务执行完毕,该任务才会进入主线程执行。
"任务列队"是一个事件的列队,IO设备完成一项任务,就在"任务队列"中添加一个事件,表示相关的异步任务可以进入"执行栈"了。主线程读取"任务队列",就是读取里面有哪些事件。"任务队列"中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入"任务队列",等待主线程读取。所谓"回调函数",就是那些会被主线程挂起的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。"任务队列"是个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空,"任务队列"上第一位的事件就自动进入主线程。但是,由于存在后文提到的"定时器"功能,主线程首先检查一下执行时间,某些事件只有到了规定的时间,才能返回主线程。
"任务队列"还可以放置定时事件,即指定某些代码在多少时间之后执行。定时器功能主要由setTimeout()和setInterval()这两个函数来完成,
它们的内部运行机制完全一样,区别在于前者指定的代码是一次性执行,后者则为反复执行。以下主要讨论setTimeou()方法:
setTimeout()接受两个参数,第一个是回调函数,第二个是推迟执行的毫秒数
console.log(1)
setTimeout(function (){
console.log(2)
}, 1000);
console.log(3)
上面代码的执行结果是1=>3=>2,因为setTimeout()将第二行推迟到1000毫秒之后执行
如果将setTimeout()的第二个参数设为0,就表示当前代码执行完(执行栈清空)以后立即执行
console.log(1)
setTimeout(function (){
console.log(2)
}, 0);
console.log(3)
上面代码的执行结果是1=>3=>2,因为只有在执行完第二行以后,系统才会去执行"任务队列"中的回调函数。
总之,setTimeout(fn, 0)的含义是,指定某个任务在主线程最早的空闲时间执行,也就是说尽可能早的执行。它在"任务队列"的尾部添加一个事件,因此要等到同步任务和"任务队列"现有的事情都处理完,才会得到执行。
需要注意的是,setTimeout()只是将事件插入了"任务队列",必须等到当前代码(执行栈)执行完,主线程(js是单线程的也就是说只有一个主线程)才会去执行它指定的回调函数,要是当前代码耗时很长,有可能要等很久
所以并没有办法保证回调函数一定会在setTimeout()指定的时间执行。
//只打印A,B不会执行,因为while一直在执行,同步代码还没执行完,异步不会执行
console.log('A');
setTimeout(function () {
console.log('B');
}, 0);
while (1) { }
来看下这段程序,执行结果是打印了四次4,因为先执行主线程的循环,setTimeout属于异步任务,被挂起了,所以是for先执行完了再执行setTimeout
for (var i = 0; i < 4; i++) {
setTimeout(function () {
console.log(i); //打印四次4
}, 1000);
}
这段程序执行的结果是1,2,3,4,是因为变量i的作用域的关系
for (let i = 0; i < 4; i++) {
setTimeout(function () {
console.log(i); //1,2,3,4
}, 1000);
}
主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。
为了更好地理解Event Loop,请看下图
stack:所有的代码都是在此空间中执行的
所有代码分类
初始化执行代码(同步代码): 包含绑定dom事件监听, 设置定时器, 发送ajax请求的代码
回调执行代码(异步代码): 处理回调逻辑
js引擎执行代码的基本流程
初始化代码===>回调代码
模型的2个重要组成部分:
事件管理模块(WebApis),回调队列(callback queue)
模型的运转流程
执行初始化代码, 将事件回调函数交给对应模块管理
当事件发生时, 管理模块会将回调函数及其数据添加到回调列队中
只有当初始化代码执行完后(可能要一定时间), 才会遍历读取回调队列中的回调函数执行
function fn1() {
console.log('fn1()')
}
fn1() // document.getElementById('btn').onclick = function () {
console.log('处理点击事件') // 触发事件的时候才会执行
} setTimeout(function () {
console.log('到点了') //
}, 2000) function fn2() {
console.log('fn2()')
}
fn2() //
H5 Web Workers多线程
为了利用多核CPU的计算功能,HTML5提出了web worker标准,允许JS脚本创建多个线程,但是子线程完全受主线程控制,且不能操作DOM,所以这个新标准并没有改变JS单线程的本质。
JavaScript的运行机制的更多相关文章
- 简述JavaScript的运行机制
想要理解JavaScript的运行机制,需要分别深刻理解以下几个点: · JavaScript的单线程机制 · 任务队列(同步任务和异步任务) · 事件和回调函数 · 定时器 · Event Loop ...
- 浅谈javascript的运行机制
积累一下这几天学的,记录一下: 一.为什么JavaScript是单线程? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.那么,为什么JavaScript不能有多个线程 ...
- 用一个小例子来谈谈javascript的运行机制
先上例子! <script type="text/javascript"> console.log('博'); setTimeout(function(){ conso ...
- [转]JavaScript线程运行机制
从开始接触js时,我们便知道js是单线程的.单线程,异步,同步,互调,阻塞等.在实际写js的时候,我们都会用到ajax,不管是原生的实现,还是借助jQuery等工具库实现,我们都知道,ajax可以实现 ...
- 浏览器UI多线程及JavaScript单线程运行机制的理解
在上一篇博客中,我对jQuery的队列(queue)机制和动画(animate)机制做了一个深入的解析,在animate的实现机制其核心是依靠queue来完成的,其中在jQuery的链式调用部分,之前 ...
- JavaScript的运行机制!!!很重要很重要!!!!!!请看大神操作!
https://juejin.im/post/59e85eebf265da430d571f89
- 深入理解JavaScript运行机制
深入理解JavaScript运行机制 前言 本文是写作在给团队新人培训之际,所以其实本文的受众是对JavaScript的运行机制不了解或了解起来有困难的小伙伴.也就是说,其实真正的原理和本文阐述的并不 ...
- JavaScript 运行机制详解:再谈Event Loop
原文地址:http://www.ruanyifeng.com/blog/2014/10/event-loop.html 一年前,我写了一篇<什么是 Event Loop?>,谈了我对Eve ...
- JavaScript 运行机制详解:深入理解Event Loop
Philip Roberts的演讲<Help, I'm stuck in an event-loop>,详细.完整.正确地描述JavaScript引擎的内部运行机制. 一.为什么JavaS ...
随机推荐
- SaltStack 安装、简单配置和远程执行
1:安装 修改hosts文件,必须保证Master端和Minion端都有完整的FQDN名示例如下: vim /etc/hosts 192.168.31.101 node2 node2.crazylin ...
- docker使用技巧小记
1.在使用docker的时候有很多人习惯使用官方镜像.有的人喜欢自己制作镜像,有的时候都是使用默认的配置启动的服务,或者自己在制作镜像的时候直接将配置文件打包到镜像里面了.有的时候会碰到要修改配置文件 ...
- InnoDB存储引擎概览
InnoDB存储引擎概览 InnoDB存储引擎以其平衡了高可靠性和高性能性而闻名遐迩,在MySQL 8.0版本中,InnoDB存储引擎是默认的存储引擎.(历史追溯从MySQL 5.5.5版本开始, ...
- 使用 libjpeg 库解压数据示例
static int MjpegDecompress( *** ) { struct jpeg_decompress_struct tDInfo; struct jpeg_error_mgr jerr ...
- 互联网推送服务原理:长连接+心跳机制(MQTT协议)
互联网推送消息的方式很常见,特别是移动互联网上,手机每天都能收到好多推送消息,经过研究发现,这些推送服务的原理都是维护一个长连接(要不不可能达到实时效果),但普通的socket连接对服务器的消耗太大了 ...
- 正则表达式--C#正则表达式的符号及例子
正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符.及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑. C#中经常会遇到要查找某一个字 ...
- c# 获取当前绝对路径
/// <summary> /// 获得当前绝对路径 /// </summary> /// <param name="strPath">指定的路 ...
- Java学习笔记之——变量与数据类型、运算符
一.变量 1.变量:变化的值 变量在代码运行期间,开辟了一块空间 .这块空间是有地址的,给这块取了个名字, 这个名字就叫做变量名,这块空间放的东西叫做变量值 2.变量的初始化: (1)先声明再赋值: ...
- Hash table lengths and prime numbers
Website:http://srinvis.blogspot.ca/2006/07/hash-table-lengths-and-prime-numbers.html This has been b ...
- 设计模式之享元模式(Flyweight)
享元模式顾名思义就是羽量级模式或者蝇级模式,形容体量小的应用,该模式主要的设计目的是为了迎合系统大量相似数据的应用而生,减少用于创建和操作相似的细碎对象所花费的成本.大量的对象会消耗高内存,享元模式给 ...