[转]javascript是单线程的深入分析

     2022-03-13     153

关键词:

Javascript是单线程的深入分析

面试的时候发现99%的童鞋不理解为什么JavaScript是单线程的却能让AJAX异步发送和回调请求,还有setTimeout也看起来像是多线程的?还有non-blocking IO, event loop等概念很不清楚。来深入分析一下:

首先看下面的代码:

function foo() {
    console.log( ‘first‘ );
    setTimeout( ( function(){ console.log( ‘second‘ ); } ), 5);
 
}
 
for (var i = 0; i < 1000000; i++) {
    foo();
}

执行结果会首先全部输出first,然后全部输出second;尽管中间的执行会超过5ms。为什么?

 

Javascript是单线程的

因为JS运行在浏览器中,是单线程的,每个window一个JS线程,既然是单线程的,在某个特定的时刻只有特定的代码能够被执行,并阻塞其它的代码。而浏览器是事件驱动的(Event driven),浏览器中很多行为是异步(Asynchronized)的,会创建事件并放入执行队列中。javascript引擎是单线程处理它的任务队列,你可以理解成就是普通函数和回调函数构成的队列。当异步事件发生时,如mouse click, a timer firing, or an XMLHttpRequest completing(鼠标点击事件发生、定时器触发事件发生、XMLHttpRequest完成回调触发等),将他们放入执行队列,等待当前代码执行完成。

 

异步事件驱动

前面已经提到浏览器是事件驱动的(Event driven),浏览器中很多行为是异步(Asynchronized)的,例如:鼠标点击事件、窗口大小拖拉事件、定时器触发事件、XMLHttpRequest完成回调等。当一个异步事件发生的时候,它就进入事件队列。浏览器有一个内部大消息循环,Event Loop(事件循环)会轮询大的事件队列并处理事件。例如,浏览器当前正在忙于处理onclick事件,这时另外一个事件发生了(如:window onSize),这个异步事件就被放入事件队列等待处理,只有前面的处理完毕了,空闲了才会执行这个事件。setTimeout也是一样,当调用的时候,js引擎会启动定时器timer,大约xxms以后执行xxx,当定时器时间到,就把该事件放到主事件队列等待处理(浏览器不忙的时候才会真正执行)。

每个浏览器具体实现主事件队列不尽相同,这不谈了。

 

浏览器不是单线程的

虽然JS运行在浏览器中,是单线程的,每个window一个JS线程,但浏览器不是单线程的,例如Webkit或是Gecko引擎,都可能有如下线程:

  • javascript引擎线程 
  • 界面渲染线程 
  • 浏览器事件触发线程 
  • Http请求线程 

很多童鞋搞不清,如果js是单线程的,那么谁去轮询大的Event loop事件队列?答案是浏览器会有单独的线程去处理这个队列。

 

Ajax异步请求是否真的异步?

很多童鞋搞不清楚,既然说JavaScript是单线程运行的,那么XMLHttpRequest在连接后是否真的异步? 
其实请求确实是异步的,这请求是由浏览器新开一个线程请求(见前面的浏览器多线程)。当请求的状态变更时,如果先前已设置回调,这异步线程就产生状态变更事件放到 JavaScript引擎的事件处理队列中等待处理。当浏览器空闲的时候出队列任务被处理,JavaScript引擎始终是单线程运行回调函数。javascript引擎确实是单线程处理它的任务队列,能理解成就是普通函数和回调函数构成的队列。

总结一下,Ajax请求确实是异步的,这请求是由浏览器新开一个线程请求,事件回调的时候是放入Event loop单线程事件队列等候处理。

 

setTimeout(func, 0)为什么有时候有用?

写js多的童鞋可能发现,有时候加一个setTimeout(func, 0)非常有用,为什么?难道是模拟多线程吗?错!前面已经说过了,javascript是JS运行在浏览器中,是单线程的,每个window一个JS线程,既然是单线程的,setTimeout(func, 0)神奇在哪儿?那就是告诉js引擎,在0ms以后把func放到主事件队列中,等待当前的代码执行完毕再执行,注意:重点是改变了代码流程,把func的执行放到了等待当前的代码执行完毕再执行。这就是它的神奇之处了。它的用处有三个:

  • 让浏览器渲染当前的变化(很多浏览器UI render和js执行是放在一个线程中,线程阻塞会导致界面无法更新渲染) 
  • 重新评估”script is running too long”警告 
  • 改变执行顺序 

例如:下面的例子,点击按钮就会显示"calculating....",如果删除setTimeout就不会。因为reDraw事件被进入事件队列到长时间操作的最后才能被执行,所以无法刷新。

<button id=‘do‘> Do long calc!</button>
<div id=‘status‘></div>
<div id=‘result‘></div>
 
 
$(‘#do‘).on(‘click‘, function(){
  
  $(‘#status‘).text(‘calculating....‘); //此处会触发redraw事件的fired,但会放到队列里执行,直到long()执行完。
  
  // without set timeout, user will never see "calculating...."
  //long();//执行长时间任务,阻塞
   
  // with set timeout, works as expected
  setTimeout(long,50);//用定时器,大约50ms以后执行长时间任务,放入执行队列,但在redraw之后了,根据先进先出原则
  
 })
  
  
  
function long(){
  var result = 0
  for (var i = 0; i<1000; i++){
    for (var j = 0; j<1000; j++){
      for (var k = 0; k<1000; k++){
        result = result + i+j+k
      }
    }
  }
  $(‘#status‘).text(‘calclation done‘) // has to be in here for this example. or else it will ALWAYS run instantly. This is the same as passing it a callback 
}

 

非阻塞js的实现(non-blocking javascript)

js在浏览器中需要被下载、解释并执行这三步。在html body标签中的script都是阻塞的。也就是说,顺序下载、解释、执行。尽管Chrome可以实现多线程并行下载外部资源,例如:script file、image、frame等(css比较复杂,在IE中不阻塞下载,但Firefox阻塞下载)。但是,由于js是单线程的,所以尽管浏览器可以并发加快js的下载,但必须依次执行。所以chrome中image图片资源是可以并发下载的,但外部js文件并发下载没有多大意义。

技术分享

要实现非阻塞js(non-blocking javascript)有两个方法:1. html5 2. 动态加载js

首先一种办法是HTML5的deferasync关键字:

defer

<script type="text/javascript" defer src="foo.js"></script>

async

<script type="text/javascript" async src="foo.js"></script>

然后第二种方法是动态加载js:

setTimeout(function(){
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = "foo.js";
    var head = true; //加在头还是尾
    if(head)
      document.getElementsByTagName("head")[0].appendChild(script);
    else
      document.body.appendChild(script);
}, 0);
 
//另外一个独立的动态加载js的函数
function loadJs(jsurl, head, callback){
    var script=document.createElement(‘script‘);
    script.setAttribute("type","text/javascript");
     
    if(callback){
        if (script.readyState){  //IE
            script.onreadystatechange = function(){
                if (script.readyState == "loaded" ||
                        script.readyState == "complete"){
                    script.onreadystatechange = null;
                    callback();
                }
            };
        } else {  //Others
            script.onload = function(){
                callback();
            };
        }
    }
    script.setAttribute("src", jsurl);
     
    if(head)
     document.getElementsByTagName(‘head‘)[0].appendChild(script);
    else
      document.body.appendChild(script);
 
}

 

转载自:http://www.cnblogs.com/Mainz/p/3552717.html


深入理解javascript运行机制

JavaScript单线程机制JavaScript的一个语言特性(也是这门语言的核心)就是单线程。什么是单线程呢?简单地说就是同一时间只能做一件事,当有多个任务时,只能按照一个顺序一个完成了再执行下一个为什么JS是单线程的呢?JS最... 查看详情

深入解析javascript异步编程

这里深入探讨下Javascript的异步编程技术。(P.S.本文较长,请准备好瓜子可乐:D)一.Javascript异步编程简介至少在语言级别上,Javascript是单线程的,因此异步编程对其尤为重要。拿nodejs来说,外壳是一层js语言,这是用户操作的层面... 查看详情

对javascript中异步同步机制以及线程深入底层了解

  今天在网上看到各种对Js异步同步单线程多线程的讨论经过前辈们的洗礼加上鄙人小小的理解就来纸上谈兵一下吧~   Js本身就是单线程的至于为什么Js是单线程的那就要追溯到Js的历史了总而言之由于Js是浏览... 查看详情

javascript单线程和异步机制

随着对JavaScript学习的深入和实践经验的积累,一些原理和底层的东西也开始逐渐了解。早先也看过一些关于js单线程和事件循环的文章,不过当时看的似懂非懂,只留了一个大概的印象:浏览器中的js程序时是单线程的。嗯,就... 查看详情

单线程的javascript

Javascript是单线程的因为JS运行在浏览器中,是单线程的,每个window一个JS线程,既然是单线程的,在某个特定的时刻只有特定的代码能够被执行,并阻塞其它的代码。而浏览器是事件驱动的(Eventdriven),浏览器中很多行为是异... 查看详情

关于javascript定时器我的一些小理解

...的那样,所以这周末我看看书查查资料,深入研究了一下JavaScript中的定时器,那么废话不多说,下面进入我们今天的正题。大家都知道JavaScript是单线程的,所以不管是定时器还是用户的操作都是需要在线程队列中排队执行的。... 查看详情

深入理解nodejs中的异步编程

简介因为javascript默认情况下是单线程的,这意味着代码不能创建新的线程来并行执行。但是对于最开始在浏览器中运行的javascript来说,单线程的同步执行环境显然无法满足页面点击,鼠标移动这些响应用户的功能。于是浏览器... 查看详情

javascript是单线程的而且是异步的机制

...就有好多的疑问 ,现在按我的理解和大家说一说一、JavaScript单线程  在浏览器中,执行JS程序只有一个线程,所以是单线程,所以执行顺序就是从上到下依次执行,同一段时间内只能有一段代码被执行。你可能会问,为什... 查看详情

javascript定时器分析

一、事件循环JavaScript是单线程,同一个时间只能做一件事情,所以执行任务需要排队。如果前一个耗时很长,那么下一个只能等待。1)两种任务为了更好的处理任务,JavaScript语言的设计者将任务分为两种:同步任务(synchronous... 查看详情

js为什么是单线程的?10分钟了解js引擎的执行机制

深入理解JS引擎的执行机制1.JS为什么是单线程的?为什么需要异步?单线程又是如何实现异步的呢?2.JS中的eventloop(1)3.JS中的eventloop(2)4.说说setTimeout首先,请牢记2点:(1)JS是单线程语言(2)JS的EventLoop是JS的执行机制。深入了解JS的执行,就... 查看详情

[转]js引擎的执行机制

...的执行机制 关于JS引擎的执行机制,首先牢记2点:.JS是单线程语言JS的EventLoop是JS的执行机制。深入了解JS的执行,就等于深入了解JS里的eventloop关于单线程相对还比较好理解,就是同时只能做一件事,JS最初设计用在浏览器... 查看详情

[转]javascript异步机制详解(代码片段)

...---------------------------------------------------------------------学习JavaScript的时候了解到JavaScript是单线程的,刚开始很疑惑,单线程怎么处理网络请求、文件读写等耗时操作呢?效率岂不是会很低 查看详情

如果 Javascript 是单线程的,那么像时钟这样的东西是如何工作的?

】如果Javascript是单线程的,那么像时钟这样的东西是如何工作的?【英文标题】:IfJavascriptissinglethreaded,howthingslikeaclockwork?【发布时间】:2020-09-1503:57:26【问题描述】:我的意思是,也许我错了,完全不理解单线程的含义,但我... 查看详情

javascript为什么是单线程-js异步与回调详解(代码片段)

JavaScript为什么是单线程JavaScript最初被设计为浏览器脚本语言,主要用途包括对页面的操作、与浏览器的交互、与用户的交互、页面逻辑处理等。如果将JavaScript设计为多线程,那当多个线程同时对同一个DOM节点进行操作... 查看详情

javascript运行机制

JavaScript运行机制阅读目录一、为什么JavaScript是单线程?二、任务队列三、事件和回调函数四、EventLoop五、定时器六、Node.js的EventLoop七、关于setTimeout的测试一、为什么JavaScript是单线程?JavaScript语言是单线程,也就是说,同一个... 查看详情

人脑是单cpu但可以带后台线程(转)

今天查了下人脑多任务处理的资料总结如下:1.人脑是单CPU的智力活应该专注2.切换任务有成本保留现场会降低成本也就是说应阶段性工作并保存成果3.熟练到可以使用直觉系统的技能是能够多任务并行的(例如熟练文员只看资料... 查看详情

javascript深入理解js闭包(转)

javascript深入理解js闭包转载  2010-07-03 作者:   我要评论闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。一、变量的作用域要理解闭包,首先必须理解Javascript特... 查看详情

javascript深入理解继承(转)

通过对继承的深入理解,更了解javascript。本人还不太写会博客,转自汤姆大叔,链接http://www.cnblogs.com/TomXu/archive/2012/01/05/2305453.html。前言JavaScript不包含传统的类继承模型,而是使用prototypal原型模型。虽然这经常被当作是JavaScrip... 查看详情