Go to comments

JS完美收官 js运行机制底层原理

一、浏览器常驻的线程

我们常说js是单线程,其实在浏览器器中包含了很多种线程

1、浏览器中都是具有这5个线程

01/

js引擎线程

用来解释和执行js代码,用户输入、网络请求


02/

GUI线程

绘制用户界面(与js主线程是互斥的)


03/

HTTP网络请求线程

前端用ajax发起异步请求(get、post等),服务器返回请求数据以后,要触发的回调函数,将回调函数推入任务队列


04/

定时器触发线程

当setTimeout 和 setTnterval等待时间结束以后,把执行函数推入任务队列中


05/

浏览器事件处理线程

将click、mouse等交互事件发生后,将这些事件放入事件队列中


2、UI主线程负责协调运转这5个线程

大部分浏览器中都是具有这5个线程的,这5个线程都是通过UI主线程负责协调运作的,UI主线程是如何来进行协调工作呢?


javaScript引擎线程里面,每一个矩形都可以当做一个任务,可以理解为函数的执行

ul主线程负责协调运转.jpg

无论是当触发浏览器事件处理线程,把任务推到js主线程之中,

还是定时器隔了一段时间,把任务放到js主线程里面去,

或者是网络请求之后数据往回传,回调函数被触发也是当做一个任务,

这几种任务最后都会推到js主线程中,然后进行处理


二、JS引擎线程和GUI线程互斥

点击按钮时,发生两件事

var oBtn = document.getElementById('btn');

oBtn.onclick = function(){

	console.log();

}

第一件事:点击按钮会触发事件,把对应事件处理函数当做一个任务,并放到JavaScript引擎线程里等待执行

第二件事:同时点击一个按钮时,有一个浏览器默认样式的变化,浏览器GUI处理渲染线程,进行按钮样式的变化的处理


不过多说其它线程,只说JavaScript引擎线程,因为它是执行js代码的灵魂


js引擎线程和GUI线程是互斥是什么意思?

比如在点击按钮之前,js在执行一个死循环,这时候由于js线程在不断的工作始终没有停,这时候再点击按钮时没有默认样式变化

var oBtn = document.getElementById('btn');

dieLoop(); // 调用死循环

oBtn.onclick = function(){

	console.log('无所谓,因为上面死循环,不会往下执行。这是js单线程所带来的状况');

}

function dieLoop(){
	while(true){
		
	}
}

在说一下互斥的意思,当点击按钮的时候,js死循环已经在执行了,js循环不执行完,点击按钮不会有样式的变化


PPT概念

JS可以操作DOM元素,进而会影响到GUI的渲染结果,因此JS引擎线程与GUI渲染线程是互斥的。也就是说当JS引擎线程处于运行状态时,GUI渲染线程将处于冻结状态。


三、JS是单线程

单线程的意思:

同一个时间段只能做一件事,

浏览器的工作基本是以线程为最小单元,js既然是单线程,说明同一时间段不可能做多个事件


多线程不好吗?(ppt概念)

js设计出来就是为了与前端用户交互,大部分工作是用来处理DOM,

假如js是多线程,同一时间段内,一个线程想要修改DOM,另一个线程想要删除DOM,这时候会衍生出很多复杂的问题,

浏览器不知道听谁的,如果引入"锁"的机制,这不就又回到了被其他语言尴尬的困境了,比如c++ java


如果需要大量数据渲染的时候怎么办?

单线程计算能力有限,比如同时十万条数据需要用GUI来渲染,并且频繁的进行dom删改,

这种情况可以配合后端进行操作,比如VUE与nodejs配合,有一个技术叫SSR,服务器端渲染技术,


铺垫完了下面是重点,Js引擎执线程的行机制是什么样子?


四、Js引擎执线程的行机制

JavaScript是基于单线程运行的,同时又可以异步执行(计算机中的异步,同一事件段你能干两件事)


既然是单线程,为什么又可以异步执行,同一时间段能干两件事?

js引擎主线程是单线程的,确实一个时间段只能干一件事情,一般来说这种既是单线程,又是异步的语言都是基于事件来驱动的


恰好浏览器就给JavaScript提供了这么一个环境,这个环境除了js引擎线程运行,还有其它线程进行配合,

由于其它线程的介入或者说其它线程的辅助,最后呈现出来js在浏览器中运行的时候,既可以同步又可以异步


JS任务的执行机制是什么样的呢?

图片1.png


首先如果任务是一个普通函数的执行,不涉及到异步网络请求、事件触发、定时器,这样一个普通的任务,就是一个同步的任务,

同步任务放到主线程中,主线程是单线程的,任务必须一个一个的执行,一个执行完再执行下一个,当主线程中的任务一个一个都执行完毕


执行完毕以后会怎么样呢?

看绿色的方块,读取任务队列中的结果,进入主线程执行


什么是任务队列?

如果一开始任务不是普通任务是异步任务,看右边紫色部分


异步任务会进入到Event Table中,先注册一下有这样的一个函数(在之后要在某一个时间段被触发),

注册完以后,

等待定时器延迟事件到了,函数或者说任务要被执行了,

或者发送网络请求数据返回到前端,要执行回调函数了


这时候

要把函数或者说任务放到Event Queue里面(事件队列里面),队列的概念是先进先出(羽毛球桶),


主线程所有的执行完以后,会看事件队列里面的任务,如果有任务就拿出来执行


上述整个不断去看任务队列的过程,就是常说的Event Loop(事件循环)。


同步任务

除了ajax异步网络请求、定时器、事件的触发以外,所有的任务都叫做同步任务,

function foo(fo){
	
	function bar(ba){
		console.log(ba)
	}
	bar(10);
	console.log(fo);
}

foo(20);


0). 代码没有执行的时候,执行栈为空栈

1). foo函数执行时,创建了一帧,这帧中包含了形参、局部变量(预编译过程),然后把这一帧压入栈中

2). 然后执行foo函数内代码,执行bar函数

3). 创建新帧,同样有形参、局部变量,也被压入栈中

4). bar函数执行完毕,弹出栈

5). foo函数执行完毕,弹出栈

6). 执行栈为空


栈为空以后,看事件队列中有没有新的任务,有任务放到栈中在执行

执行栈其实相当于js主线程,也就是说执行栈同一时间段只能干一件事件


异步任务

Ajax异步任务会进入Event Table里面,注册回调函数success

$.ajax({
	url: 'localhost:/js/demo.json',
	data: {},
	success: function (data) {
		console.log(data);
	}
});

console.log(‘run’);

1). Ajax异步任务会进入Event Table里面,注册回调函数success

2). 一定会先执行 console.log('run')

3). ajax事件请求数据回来,http网络请求线程把任务放入Event Queue队列里以后,

4). 主线程(调用栈)也空了,读取任务队列到栈里面,再执行success函数


五、重新理解定时器

var starTime = +new Date();

var timer = setInterval(function(){

	console.log('这里是定时器:' , +new Date() - starTime);
	clearInterval(timer);


}, 0);

sleep(100000000);

function sleep(t){

	for(var i = 0; i <= t; i++){
		if(i === t){
			console.log('已循环' + i);
		}
	}
}

setTimeout的等待时间结束后,并不是直接执行的而是先推入浏览器的一个任务队列,在同步队列结束后在依次调用任务队列中的任务。

setTimeout(function(){}, 0)Js主线程中的执行栈为空时,0毫秒实际上也达不到的,根据HTML标准,最低4毫秒。

setInterval是每隔一段时间把任务放到Event Queue之中


定时器不准,

定时器底部是红黑数写的,

js的执行机制也导致定时器不准



Leave a comment 0 Comments.

Leave a Reply

换一张