搜索
首页web前端js教程JavaScript中setTimeout之单线程

JavaScript中setTimeout之单线程

Dec 05, 2017 pm 01:23 PM
javascriptjssettimeout

一直以来,大家都在说Javascript是单线程,浏览器无论在什么时候,都且只有一个线程在运行JavaScript程序、JavaScript中setTimeout方法用处比较多,通常用在页面刷新了、延迟执行了等等、但是很多javascript新手对setTimeout的用法还是不是很了解、今天就带大家介绍下JavaScript中setTimeout之单线程!

但是,不知道大家有疑问没——就是我们在编程过程中的setTimeout(类似的还有setInterval、Ajax),不是异步执行的吗?!!

例如:

<!DOCTYPE html>
    <head>
        <title>setTimeout</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
        <script>
            console.log("a");            
            //利用setTimeout延迟执行匿名函数            
            setTimeout(function(){
                console.log("b");
            },100);
            console.log("c");       
             </script>
    </body>
    </html>

运行代码,打开chrome调试器,得如下结果

 

这个结果很容易理解,因为我setTimeout里的内容是在100ms后执行的嘛,当然是先输出a,再输出c,100ms后再输出setTimeout里的b嘛。

咦,那Javascript这不就不是单线程了嘛,这不就可以实现多线程了?!!

其实,不是的。setTimeout没有打破JavaScript的单线程机制,它其实还是单线程。

为什么这么说呢,那就得理解setTimeout到底是个怎么回事。

请看下面的代码,猜结果:

<!DOCTYPE html>
    <head>
        <title>setTimeout</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
        <script>
            var date = new Date();            
            //打印才进入时的时间            
            console.log(&#39;first time: &#39; + date.getTime());            
            //一秒后打印setTimeout里匿名函数的时间            
            setTimeout(function(){                
            var date1 = new Date();
                console.log(&#39;second time: &#39; + date1.getTime() );
                console.log( date1.getTime() - date.getTime() );
            },1000);            
            //重复操作
            for(var i=0; i < 10000 ; i++){
                console.log(1);
            }        
            </script>
    </body>
    </html>

看了上面的代码,猜猜输出的结果是多少呢?1000毫秒?

我们打开chrome调试器,见下图

纳尼,怎么不是1000毫秒呢?!!!

我们再看看下面的代码:

<!DOCTYPE html>
    <head>
        <title>setTimeout</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
        <script>
            //一秒后执行setTimeout里的匿名函数,alert下            
            setTimeout(function(){
                alert("monkey");
            },1000);            
            while(true){};        
            </script>
    </body></html>

运行代码后!

我靠,怎么一直刷新,浏览器卡死了呢,并且没有alert!!

按道理,即使我while无限循环,在1秒后也得alert一下啊。

种种问题皆一个原因,JavaScript是单线程

要记住JavaScript是单线程,setTimeout没有实现多线程,它背后的真相是这样滴:

JavaScript引擎是单线程运行的,浏览器无论在什么时候都只且只有一个线程在运行JavaScript程序。

浏览器的内核是多线程的,它们在内核控制下相互配合以保持同步,一个浏览器至少实现三个常驻线程:JavaScript引擎线程,GUI渲染线程,浏览器事件触发线程

    *JavaScript引擎是基于事件驱动单线程执行的,JavaScript引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JavaScript线程在运行JavaScript程序。

    *GUI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(Reflow)时,该线程就会执行。但需要注意,GUI渲染线程与JavaScript引擎是互斥的,当JavaScript引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JavaScript引擎空闲时立即被执行。

    *事件触发线程,当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JavaScript引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeout、也可来自浏览器内核的其他线程如鼠标点击、Ajax异步请求等,但由于JavaScript的单线程关系所有这些事件都得排队等待JavaScript引擎处理(当线程中没有执行任何同步代码的前提下才会执行异步代码)。

so,通过以上讲解,以上种种问题迎刃而解。

二、setTimeout那些事儿之延迟时间为0

当setTimeout的延迟时间为0时,大家想想它会怎么执行呢?

例如下面的代码:

<!DOCTYPE html>
    <head>
        <title>setTimeout</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
        <script>
            console.log(&#39;a&#39;);
            setTimeout(function(){
                console.log(&#39;b&#39;);
            },0);
            console.log(&#39;c&#39;);
            console.log(&#39;d&#39;);        </script>
    </body></html>

运行代码结果如下:

假设你已经知道Javascript单线程的运行原理了。那么,可能会有这样的疑问:setTimeout的时间为0,都没加到处理队列的末尾,怎么会晚执行呢?不应该立即执行吗?

我的理解是,就算setTimeout的时间为0,但是它仍然是setTimeout啊,原理是不变的。所以会将其加入到队列末尾,0秒后执行。

况且,经过查找资料发现,setTimeout有一个最小执行时间,当指定的时间小于该时间时,浏览器会用最小允许的时间作为setTimeout的时间间隔,也就是说即使我们把setTimeout的毫秒数设置为0,被调用的程序也没有马上启动。

这个最小的时间间隔是多少呢?

这和浏览器及操作系统有关。在John Resig的《Javascript忍者的秘密》一书中提到--Browsers all have a 10ms minimum delay on OSX and a(approximately) 15ms delay on Windows.(在苹果机上的最小时间间隔是10毫秒,在Windows系统上的最小时间间隔大约是15毫秒),另外,MDC中关于setTimeout的介绍中也提到,Firefox中定义的最小时间间隔(DOM_MIN_TIMEOUT_VALUE)是10毫秒,HTML5定义的最小时间间隔是4毫秒。

说了这么多,setTimeout的延迟时间为0,看来没什么意义嘛,都是放在队列后执行嘛。

非也,天生我材必有用,就看你怎么用咯。抛砖迎玉下。

1、可以用setTimeout的延迟时间为0,模拟动画效果哦。

详情请见下代码:

<!DOCTYPE html>
    <head>
        <title>setTimeout</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
        <div id="container" style="width:100px;height:100px;border:1px solid black;"></div>
        <div id="btn" style="width:40px;height:40px;line-height:40px;margin-top:20px;background:pink;">click</div>
        <script>
            window.onload = function(){
                var con = document.getElementById(&#39;container&#39;);
                var btn = document.getElementById(&#39;btn&#39;); 
                //Params: i 为起始高度,num为预期高度
                function render(i, num) {
                    i++; 
                    con.style.height = i + &#39;px&#39;;
                    //亮点在此
                    if(i < num){
                        setTimeout(function() {
                            render(i, num);
                        },0);
                    }
                    else {
                        con = null;
                        btn = null;
                    }
                };
                btn.onclick = function(){
                    render(100, 200);
                };
            };
        </script>
    </body>
</html>


由于是动画,所以想看其效果,还请各位看官运行下代码哦。

代码第19行中,利用setTimeout,在每一次render执行完成(给高度递增1)后,由于Javascript是单线程,且setTimeout里的匿名函数会在render执行完成后,再执行render。所以可以实现动画效果。

2、可以用setTimeout的延迟时间为0,实现捕获事件哦。

当我们点击子元素时,我们可以利用setTimeout的特性,来模拟捕获事件。

请见如下代码:

<!DOCTYPE html>
    <head>
        <title>setTimeout</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <style>    
            #parent {
                width:100px;
                height:100px;
                border:1px solid black;
            }   
            #child {
                width:50px;
                height:50px;
                background:pink;
            }
        </style>
    </head>
    <body>
        <p id="parent">
            <p id="child"></p>
        </p>
        <script>
            //点击子元素,实现子元素的事件在父元素触发后触发            
            window.onload = function(){                
            var parent = document.getElementById(&#39;parent&#39;); 
                var child = document.getElementById(&#39;child&#39;);
                parent.onclick = function(){
                    console.log(&#39;parent&#39;);
                }
                child.onclick = function(){                    
                //利用setTimeout,冒泡结束后,最后输出child                    
                setTimeout(function(){
                        console.log(&#39;child&#39;);   
                    },0);
                }
                parent = null;
                child = null;   
            }        </script>
    </body></html>

 执行代码,点击粉红色方块,输出结果:

 

三、setTimeout那些事儿之this

说到this,对于它的理解就是:this是指向函数执行时的当前对象,倘若没有明确的当前对象,它就是指向window的。

好了,那么我们来看看下面这段代码:

<!DOCTYPE html>
    <head>
        <title>setTimeout</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
        <script>
           var name = &#39;!!&#39;;           
           var obj = {
               name:&#39;monkey&#39;,
               print:function(){
                   console.log(this.name);
               },
               test:function(){                   
               //this.print                   
               setTimeout(this.print,1000);
               }
           }
           obj.test();        
           </script>
    </body></html>

通过chrome调试器,查看输出结果:

 

咦,它怎么输出的是”!!”呢?不应该是obj里的”monkey”吗?!!

这是因为setTimeout中所执行函数中的this,永远指向window。

不对吧,那上面代码中的setTimeout(this.print,1000)里的this.print怎么指向的是obj呢?!!

注意哦,我这里说的是“延迟执行函数中的this”,而不是setTimeout调用环境下的this。

什么意思?

setTimeout(this.print,1000),这里的this.print中的this就是调用环境下的;

而this.print=function(){console.log(this.name);},这个匿名函数就是setTimeout延迟执行函数,其中的this.name也就是延迟执行函数中的this啦。

嘿嘿,这下明白了吧。

var age = 24;function Fn(){    this.age = 18;
    setTimeout(function(){        //this代表window
        console.log(this);        //输出24,而不是Fn的18
        console.log(this.age);
    },1000);
}new Fn();

咦,那有个疑问,比如我想在setTimeout延迟执行函数中的this指向调用的函数呢,而不是window?!!我们该怎么办呢。

常用的方法就是利用that。

that?

对,that。利用闭包的知识,让that保证你传进去的this,是你想要的。

详情见下:

var age = 24;function Fn(){    //that在此
    var that = this;    this.age = 18;
    setTimeout(function(){
        console.log(that);
        console.log(that.age);
    },1000);
}new Fn();

还有一种方法就是,利用bind。

如下:

var age = 24;function Fn(){    this.age = 18;    //bind传入this
    setTimeout(function(){
        console.log(this);
        console.log(this.age);
    }.bind(this),1000);
}new Fn();

总结:

本文通过实例详细的介绍了关于JavaScript中setTimeout之单线程,相信小伙们对setTimeout有了进一步的了解,希望对你的工作有所帮助!

相关推荐:

JavaScript中setTimeout()的使用详解

JavaScript定时器中关于setTimeout()与setInterval()的详解

javascript函数setTimeout带参数用法实例详解

从setTimeout来谈谈说事件循环模型

以上是JavaScript中setTimeout之单线程的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
Python vs. JavaScript:您应该学到哪种语言?Python vs. JavaScript:您应该学到哪种语言?May 03, 2025 am 12:10 AM

选择Python还是JavaScript应基于职业发展、学习曲线和生态系统:1)职业发展:Python适合数据科学和后端开发,JavaScript适合前端和全栈开发。2)学习曲线:Python语法简洁,适合初学者;JavaScript语法灵活。3)生态系统:Python有丰富的科学计算库,JavaScript有强大的前端框架。

JavaScript框架:为现代网络开发提供动力JavaScript框架:为现代网络开发提供动力May 02, 2025 am 12:04 AM

JavaScript框架的强大之处在于简化开发、提升用户体验和应用性能。选择框架时应考虑:1.项目规模和复杂度,2.团队经验,3.生态系统和社区支持。

JavaScript,C和浏览器之间的关系JavaScript,C和浏览器之间的关系May 01, 2025 am 12:06 AM

引言我知道你可能会觉得奇怪,JavaScript、C 和浏览器之间到底有什么关系?它们之间看似毫无关联,但实际上,它们在现代网络开发中扮演着非常重要的角色。今天我们就来深入探讨一下这三者之间的紧密联系。通过这篇文章,你将了解到JavaScript如何在浏览器中运行,C 在浏览器引擎中的作用,以及它们如何共同推动网页的渲染和交互。JavaScript与浏览器的关系我们都知道,JavaScript是前端开发的核心语言,它直接在浏览器中运行,让网页变得生动有趣。你是否曾经想过,为什么JavaScr

node.js流带打字稿node.js流带打字稿Apr 30, 2025 am 08:22 AM

Node.js擅长于高效I/O,这在很大程度上要归功于流。 流媒体汇总处理数据,避免内存过载 - 大型文件,网络任务和实时应用程序的理想。将流与打字稿的类型安全结合起来创建POWE

Python vs. JavaScript:性能和效率注意事项Python vs. JavaScript:性能和效率注意事项Apr 30, 2025 am 12:08 AM

Python和JavaScript在性能和效率方面的差异主要体现在:1)Python作为解释型语言,运行速度较慢,但开发效率高,适合快速原型开发;2)JavaScript在浏览器中受限于单线程,但在Node.js中可利用多线程和异步I/O提升性能,两者在实际项目中各有优势。

JavaScript的起源:探索其实施语言JavaScript的起源:探索其实施语言Apr 29, 2025 am 12:51 AM

JavaScript起源于1995年,由布兰登·艾克创造,实现语言为C语言。1.C语言为JavaScript提供了高性能和系统级编程能力。2.JavaScript的内存管理和性能优化依赖于C语言。3.C语言的跨平台特性帮助JavaScript在不同操作系统上高效运行。

幕后:什么语言能力JavaScript?幕后:什么语言能力JavaScript?Apr 28, 2025 am 12:01 AM

JavaScript在浏览器和Node.js环境中运行,依赖JavaScript引擎解析和执行代码。1)解析阶段生成抽象语法树(AST);2)编译阶段将AST转换为字节码或机器码;3)执行阶段执行编译后的代码。

Python和JavaScript的未来:趋势和预测Python和JavaScript的未来:趋势和预测Apr 27, 2025 am 12:21 AM

Python和JavaScript的未来趋势包括:1.Python将巩固在科学计算和AI领域的地位,2.JavaScript将推动Web技术发展,3.跨平台开发将成为热门,4.性能优化将是重点。两者都将继续在各自领域扩展应用场景,并在性能上有更多突破。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

将Eclipse与SAP NetWeaver应用服务器集成。

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器

Dreamweaver Mac版

Dreamweaver Mac版

视觉化网页开发工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境