一、setTimeout那些事儿之单线程

一直以来,大家都在说Javascript是单线程,浏览器无论在什么时候,都且只有一个线程在运行JavaScript程序。

但是,不知道大家有疑问没——就是我们在编程过程中的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('first time: ' + date.getTime());            //一秒后打印setTimeout里匿名函数的时间            setTimeout(function(){                var date1 = new Date();                console.log('second time: ' + 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('a');            setTimeout(function(){                console.log('b');            },0);            console.log('c');            console.log('d');        </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,模拟动画效果哦。

详情请见下代码:

 1 <!DOCTYPE html> 2     <head> 3         <title>setTimeout</title> 4         <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> 5     </head> 6     <body> 7         <div id="container" style="width:100px;height:100px;border:1px solid black;"></div> 8         <div id="btn" style="width:40px;height:40px;line-height:40px;margin-top:20px;background:pink;">click</div> 9         <script>10             window.onload = function(){11                 var con = document.getElementById('container');12                 var btn = document.getElementById('btn'); 13                 //Params: i 为起始高度,num为预期高度14                 function render(i, num) {15                     i++; 16                     con.style.height = i + 'px';17                     //亮点在此18                     if(i < num){19                         setTimeout(function() {20                             render(i, num);21                         },0);22                     }23                     else {24                         con = null;25                         btn = null;26                     }27                 };28                 btn.onclick = function(){29                     render(100, 200);30                 };31             };32         </script>33     </body>34 </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>        <div id="parent">            <div id="child"></div>        </div>        <script>            //点击子元素,实现子元素的事件在父元素触发后触发            window.onload = function(){                var parent = document.getElementById('parent');                 var child = document.getElementById('child');                parent.onclick = function(){                    console.log('parent');                }                child.onclick = function(){                    //利用setTimeout,冒泡结束后,最后输出child                    setTimeout(function(){                        console.log('child');                       },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 = '!!';           var obj = {               name:'monkey',               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();

转载于:https://blog.51cto.com/zhangtaoze/1917052

setTimeout那些事儿相关推荐

  1. util.promisify 的那些事儿

    util.promisify是在node.js 8.x版本中新增的一个工具,用于将老式的Error first callback转换为Promise对象,让老项目改造变得更为轻松. 在官方推出这个工具 ...

  2. 【前端应该知道的那些事儿】运动学基础

    [写在前面的话:] 前不久刚看到过一句话:说好的技术文章应该让读者感觉增加信心,而不是失去信心. 有感于这句话是因为以前觉得发一些貌似高深的,看起来nb的东西才算一篇好博文,可是多少有点炫技的成分.可 ...

  3. 「前端那些事儿」② 极限性能优化

    前言 前端的工作并不仅仅是实现「视觉&交互稿」,想要开发一个高性能易维护的「完美」站点并未易事,针对前端的性能优化贯穿着项目开发的始终,绝不是交互口中的「拖拽工人」! 然而前端优化策略却是一件 ...

  4. setTimeout、setInterval、promise、async/await的顺序详解(多种情况,非常详细~)

    本文很长,列举的情况很多. 在阅读本文之前,如果您有充足的时间,请新建一个项目与本文一同实践. 每段代码都有对应的解释,但是自己动手尝试印象才会更深哦~ setInterval:表示多久执行一次,需要 ...

  5. webassembly类型_WebAssembly 那些事儿

    WebAssembly 那些事儿 什么是 WebAssembly? WebAssembly 是除 JavaScript 以外,另一种可以在网页中运行的编程语言,并且相比之下在某些功能和性能问题上更具优 ...

  6. html5 settimeout,计时器setTimeout()

    setTimeout(代码,延迟时间); 参数说明: 1. 要调用的函数或要执行的代码串. 2. 延时时间:在执行代码前需等待的时间,以毫秒为单位(1s=1000ms). 当我们打开网页3秒后,在弹出 ...

  7. 计算机网络技术社团纳新海报,精品社团纳新 | 加入计算机协会和我一起做些有意义的事儿吧~...

    原标题:精品社团纳新 | 加入计算机协会和我一起做些有意义的事儿吧~ 我们是谁? 计算机协会 社团 简介 社团名称:计算机协会 创办时间:2012年 组成部门:技术部,宣传部,秘书部,外联部,策划部. ...

  8. Linux那些事儿 之 戏说USB(33)字符串描述符

    关于字符串描述符,前面的前面已经简单描述过了,地位仅次于设备/配置/接口/端点四大描述符,那四大设备必须得支持,而字符串描述符对设备来说则是可选的. 这并不是就说字符串描述符不重要,对咱们来说,字符串 ...

  9. Linux那些事儿 之 戏说USB(28)设备的生命线(十一)

    现在已经使用GET_DESCRIPTOR请求取到了包含一个配置里所有相关描述符内容的一堆数据,这些数据是raw的,即原始的,所有数据不管是配置描述符.接口描述符还是端点描述符都彼此的挤在一起,所以得想 ...

最新文章

  1. java变量代码_java 变量代码
  2. 海啸级后浪!“天才少年”曹原再次连发2篇Nature!
  3. 第十六届全国大学生智能车竞赛线上赛点赛道审核 - 山东赛区
  4. 自己挖的坑自己填--JVM报内存溢出
  5. java连接access2013数据库_滴水穿石–Java连接Access数据库及其操作
  6. qwebkit 服务器调用本地html方法,用qt的QWebkit类调用谷歌地图
  7. js 执行环境 活动对象 变量对象 作用域链的理解
  8. 2002-2003 ACM-ICPC Northeastern European Regional Contest (NEERC 02) A Amusing Numbers (数学)
  9. 项目入口_住宅小区入口就该这么设计,说得好仔细!
  10. rust: linker-link-exe-not-found
  11. Python问题:UnsortedIndexError: ‘MultiIndex slicing requires the index to be lexsorted: slicing on lev
  12. java实现身份证号码的严格校验!
  13. 从Ping-Pong消息学习Gossip协议
  14. 嵌入式软件工程师自学之路
  15. libapache2-mod-php5 apache,Debian Etch libapache2-mod-php5 with bundled libgd
  16. 科大讯飞 java Web api语音生成和语音识别参考
  17. 深入理解SHA系列加密算法
  18. 重置SMC和NVRAM解决MacBookPro卡顿问题
  19. 硕博电子-AGV小车控制器
  20. android 银联插件,Android版添加phonegap-银联支付插件教程

热门文章

  1. AtCoder Grand Contest #026 D - Histogram Coloring
  2. django入门与实践 3-1 环境搭建
  3. chrome console js多行输入
  4. Android 的 生命周期_ 界面的切换 和 吐司
  5. PHP中数组的三种排序方法
  6. C#里巧用DateTime预设一些可选的日期范围(如本年度、本季度、本月等)
  7. mysql中数据库覆盖导入的几种方式
  8. 完整opencv(emgucv)人脸、检测、采集、识别、匹配、对比
  9. MFC+OPENCV+显示MAT类型图像
  10. OpenCV:详解掩膜mask