js异步再看

之前有一篇《异步运行》,偶然写了一段代码对得到的结果不是很理解,重新查资料得到这篇文章。代码如下:

<body>
        this is test
	<script>
		setTimeout(function(){
			alert(100)
		},0);
	</script>
	<script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js'></script>
//这里addthis.js的引用是比较耗时的
        <script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
//这个引用是很快的
	<script>
		for(var i = 0;i<10000;i++){
			console.log(i);
		}
		alert(5)
	</script>

</body>

 这段代码的结果是:

①页面渲染出‘this is test’

②弹出100

③弹出5

如果把引入addthis.js这段代码注释掉那结果将会是:

①弹出5

②页面渲染出‘this is test’

③弹出100

为什么会出现这种结果呢?

接下来慢慢解释。

首先明确一点:JavaScript引擎是单线程运行的,浏览器无论在什么时候都只有一个线程在运行JavaScript程序

js设计成单线程的原因《异步运行》中讲过,那么单线程的JavaScript引擎是怎么配合浏览器内核处理定时器和响应浏览器事件等异步任务的呢?

浏览器内核允许多个线程异步执行,这些线程在内核控制下相互配合以保持同步。假如某一浏览器内核的实现至少有三个常驻线程:javascript引擎线程,界面渲染线程,浏览器事件触发线程,除这些外,还有一些执行完就终于的线程,如http请求线程,这些异步线程都会产生不同的异步事件,下面通过一个图来阐明单线程的JavaScript引擎与另外那些线程是怎样互动通信的。虽然每个浏览器内核实现细节不同,但这其中的调用原理都是大同小异

 

 

浏览器中的JavaScript引擎是基于事件驱动的,这里的事件可看作是浏览器派给他的各种任务,这些任务可以源自JS引擎当前执行的代码块,如调用setTimeout添加一个任务,也可来自浏览器内核的其他线程,如界面元素鼠标点击事件,定时触发器事件到达通知,异步请求状态变更通知等。从代码角度来看,任务实体就是各种回调函数,JS引擎一直等待着任务队列中任务的到来,由于单线程的关系,这些任务得进行排队,一个接着一个被引擎处理。

上图t1,t2...tn表示不同的时间点,tn下面对应的小方块代表该时间点的任务,假设现在是t1时刻,引擎运行在t1对应的任务方块代码内,在这个时间点内,我们来描述一下浏览器内核其他线程的状态

t1时刻:

GUI渲染线程:

该线程复制渲染浏览器界面HTML元素,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。GUI渲染线程跟JavaScript引擎线程是互斥的,这容易理解,因为JavaScript脚本可操纵DOM元素,在修改这些元素属性同时渲染界面,那么渲染前后获得的元素数据可能就不一致了。

在JavaScript引擎运行脚本期间,浏览器渲染线程都是出于挂起状态的,也就是被“冻结了”

所以,在脚本中执行对界面进行更新操作,如添加节点,删除节点或改变节点的外观等更新并不会立即体现出来,这些操作将保存在一个队列中,待JavaScript引擎空闲时才有机会渲染出来。

GUI事件触发线程:

JS脚本的执行不影响html元素事件的触发,在t1时间段内,首先是用户点击了一个鼠标键,点击被浏览器事件触发线程捕捉后形成一个鼠标点击事件,由图可知,对于JS引擎线程来说,这事件是由其他线程异步传到任务队列队尾的,由于引擎正在处理t1时的任务,这个鼠标点击事件正在等待处理

定时触发线程:

定时事件也是异步事件,它必须依赖外部来计时并触发定时事件。

上图所示,在t1任务执行的时间内,继鼠标点击事件触发后,先前设置的setTimeout定时也到达了,此刻对JS引擎来说,定时触发线程产生了一个异步定时事件并放到任务队列中,该事件被排到点击事件回调之后,等待处理。

同理,在t1任务执行时间内,setInterval定时器任务也被添加了

可见,假如t1任务执行时间非常长,远大于setInterval的定时间隔,那么定时触发器就会源源不断的产生异步定时事件并放到任务队列队尾而不管它们的‘前辈’是否已被处理。一旦前面的任务处理完,这些队列中的定时任务就不间断的被执行(而不会有时间间隔),这是因为,对于JS引擎来说,在处理队列中的各任务时处理方式都是一样的,只是处理的次序不同而已。

如果队列非空,引擎就从队列头取出一个任务,直到该任务处理完(即返回)后引擎接着取并运行下一个任务,在任务没返回前队列中的其他任务是没法被执行的

现在可以试着解释下文章开头的代码,这些代码每个部分都是有用的,包括for循环和两个js文件的引用

猜你喜欢

转载自xiaoxiaoher.iteye.com/blog/2379099
今日推荐