zoukankan      html  css  js  c++  java
  • 我理解的javascript单线程机制

    废话不多说,我们先来看几个例子:

    1.

     1 setTimeout(function(){console.log(1);}, 0); 2 console.log(2); 

    result:  2 1
     
    2.
     1 console.log(100); 2 setTimeout(function(){console.log(200);},0); 3 console.log(300); 
    result:  100 300 200
     
    3.
    1 function foo(){
    2     console.log('first');
    3     setTimeout((function(){console.log('second')}),0);
    4 }
    5 for(var i=0; i<10; i++){
    6     foo();
    7 }

    result:  先全部输出first,然后全部输出second

    4.

     1 console.log(10); 2 setTimeout(function(){console.log(20);},0); 3 while(true){console.log(30);} 

    result:  执行的结果是10 30 , 然后浏览器假死,20永远不会被打印出来

    5.
    1 console.log(10);
    2 setTimeout(function(){while(true){console.log(30);}},0);
    3 setTimeout(function(){console.log(20);},10);
    4 console.log(40);

    result:  执行的结果: 10,40,30, 然后浏览器假死,20永远不会被打印出来

    看完上面的几个例子后,是不是觉得,我天,666. 长见识了!!!

    其实,这里展现的就是javascript的单线程机制。JavaScript语言的一大特点就是单线程,换句话说就是,同一个时间只能做一件事。那么,JavaScript为啥不能有多个线程呢?这样能提高效率啊。JavaScript作为浏览器脚本语言,它的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,如果JavaScript同时有了两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,那么这时浏览器到底应该以哪个线程为准?所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

    javascript中的所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

    "任务队列"是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空,"任务队列"上第一位的事件就自动进入主线程。

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

    setTimeout(fn,0),它的的含义是,指定某个任务在主线程最早可得的空闲时间执行,也就是说,尽可能早得执行。它在"任务队列"的尾部添加一个事件,因此要等到同步任务和"任务队列"现有的事件都处理完,才会得到执行。这里有一点值得注意的地方是,setTimeout()只是将事件插入了"任务队列",必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定会在setTimeout()指定的时间执行。

    那么,setTimeout(fn,0)到底有什么用呢?

    它的神奇之处是改变了代码流程,把fn的执行放到了等待当前的代码执行完毕再执行。所以我们常把它用来改变代码执行的顺序。

    为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

    下面看一个web worker多线程的例子:

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <title></title>
     6 </head>
     7 <body>
     8 <p>Fibonacci数列是比较经典的数学规律,它以递归的方法定义:</p>
     9 <p>F0 = 0</p>
    10 <p>F1 = 1</p>
    11 <p>Fn = F(n-1) + F(n-2) (n>=2, n属于整数)</p>
    12 
    13 
    14 <script type="text/javascript">
    15     window.onload = function(){
    16         var worker = new Worker('fibonacci.js');
    17         worker.postMessage(40);
    18         var timer = (new Date()).valueOf();
    19         console.log('开始计算:n=40','当前时间:'+timer);
    20         worker.onmessage = function(e){
    21             var timer2 = (new Date()).valueOf();
    22             console.log('计算结果:'+ e.data+' 计算完成时间:'+timer2+"计算用时:"+(timer2 - timer));
    23         };
    24 
    25         setTimeout(function(){
    26             console.log('由于js的单线程机制,本来由于浏览器假死,定时器函数在计算数列时不会执行,' +
    27                     '但这里用了workers多线程,前面计算fibonacci数列浏览器并没有假死,' +
    28                     '所以定时器函数在计算数列时执行了,但还是在主线程代码执行完成之后',
    29                     '当前执行时间:'+(new Date()).valueOf());
    30         });
    31 
    32         // js主线程
    33         console.log("我在计算数列的时候执行了","当前时间:"+(new Date()).valueOf());
    34     };
    35 
    36  
    37 </script>
    38 </body>
    39 </html>
    fibonacci.js:
     1 /**
     2  * Created by jessietang on 12/8/2016.
     3  */
     4 //此线程用来计算fibonacci数列
     5 var fibonacci = function(n){
     6     return n<2 ? n : arguments.callee(n-1) + arguments.callee(n-2);
     7 };
     8 onmessage = function(e){
     9     var n = parseInt(e.data,10);
    10     postMessage(fibonacci(n));
    11 };

    输出结果:

    开始计算:n=40 当前时间:1481191712748
    我在计算数列的时候执行了 当前时间:1481191712749
    由于js的单线程机制,本来本来由于浏览器假死,定时器函数在计算数列时不会执行,但这里用了workers多线程,前面计算fibonacci数列浏览器并没有假死,所以定时器函数在计算数列时执行了,但还是在主线程代码执行完成之后 当前执行时间:1481191712770
    计算结果:102334155 计算完成时间:1481191724690计算用时:11942
    相信看完输出结果就大致明白了。
     
    说明:此篇博客借鉴了阮一峰老师的这篇博客的一些理论:http://www.ruanyifeng.com/blog/2014/10/event-loop.html。个人理解,纯属瞎玩儿,欢迎提出不同意见一起讨论
  • 相关阅读:
    JSP中Session的使用
    深入了解父类引用指向子类
    Gamma校正及其OpenCV实现
    cocos2d-x3.0之请求网络(phpserver)
    Quartz使用-入门使用(java定时任务实现)
    ExtJs--15--Ext.is*各种类型推断的方法,简单看源代码就能够明确了
    小谈边界问题
    VS2010旗舰版安装图解
    LSPCI具体解释分析
    兔子--gradle安装和配置
  • 原文地址:https://www.cnblogs.com/xiayu25/p/6242198.html
Copyright © 2011-2022 走看看