setTimeout那些事儿
| 一、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,模拟动画效果哦。
详情请见下代码:
<!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('container');
var btn = document.getElementById('btn');
//Params: i 为起始高度,num为预期高度
function render(i, num) {
i++;
con.style.height = i + 'px';
//亮点在此
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>
<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();
好了时间也不早,晚安~另外,清明快乐哦
setTimeout那些事儿的更多相关文章
- 细说Promise
一.前言 JavaScript是单线程的,固,一次只能执行一个任务,当有一个任务耗时很长时,后面的任务就必须等待.那么,有什么办法,可以解决这类问题呢?(抛开WebWorker不谈),那就是让代码异步 ...
- call,apply,bind
一.call&apply call, apply都属于Function.prototype的方法,因为属于Function.prototype,所以每个Function对象实例,也就是每个方法 ...
- 浅谈webWorker
一.webWorker之初体验 在"setTimeout那些事儿"中,说到JavaScript是单线程.也就是同一时间只能做同一事情. 也好理解,作为浏览器脚本语言,如果JavaS ...
- webWorker
一.webWorker之初体验 在"setTimeout那些事儿"中,说到JavaScript是单线程.也就是同一时间只能做同一事情. 也好理解,作为浏览器脚本语言,如果JavaS ...
- setTimeout 和 throttle 那些事儿
document.querySelector('#settimeout').onclick= function () { setTimeout(function () { console.log('t ...
- setTimeout和setInterval的注意事项
精准问题 setTimeout的问题在于它并不是精准的,例如使用setTimeout设定一个任务在10ms后执行,但是在9ms后,有一个任务占用了5ms的cpu时间片,再次轮到定时器执行时,时间已经过 ...
- ES5和ES6那些你必须知道的事儿(三)
ES5和ES6那些你必须知道的事儿 ES6新增的东西 一.块级作用域:关键字let,常量const let与var的区别: a.不会进行变量声明提升 b.变量不允许被重复定义 c.变量不允许被删除 d ...
- util.promisify 的那些事儿
util.promisify是在node.js 8.x版本中新增的一个工具,用于将老式的Error first callback转换为Promise对象,让老项目改造变得更为轻松. 在官方推出这个工具 ...
- 一道题理解setTimeout,Promise,async/await以及宏任务与微任务
今天看到这样一道面试题: //请写出输出内容 async function async1() { console.log('async1 start'); await async2(); consol ...
随机推荐
- jq跑马灯效果
这几天公司产品有个无缝循环滚动的广告跑马灯要做,最开始想到的是<marquee>标签,但在PC端正常,在安卓广告屏上却怎么都跑不动,后来用的css3的animation,结果也是PC端及其 ...
- 红黑树——算法导论(15)
1. 什么是红黑树 (1) 简介 上一篇我们介绍了基本动态集合操作时间复杂度均为O(h)的二叉搜索树.但遗憾的是,只有当二叉搜索树高度较低时,这些集合操作才会较快:即当树的高度较高(甚至一种极 ...
- jQuery学习之路(2)-DOM操作
▓▓▓▓▓▓ 大致介绍 jQuery作为JavaScript库,继承并发扬了JavaScript对DOM对象操作的特性,使开发人员能方便的操作DOM对象. ▓▓▓▓▓▓ jQuery中的DOM操作 看 ...
- C#多线程之线程池篇3
在上一篇C#多线程之线程池篇2中,我们主要学习了线程池和并行度以及如何实现取消选项的相关知识.在这一篇中,我们主要学习如何使用等待句柄和超时.使用计时器和使用BackgroundWorker组件的相关 ...
- C语言动态走迷宫
曾经用C语言做过的动态走迷宫程序,先分享代码如下: 代码如下: //头文件 #include<stdio.h> #include<windows.h>//Sleep(500)函 ...
- CSS 3学习——box-sizing和背景
box-sizing 在CSS 2中设置元素的width和height仅仅是设置了元素内容区的宽和高,元素实际的尺寸是margin + border + padding + 内容区. CSS 3(截止 ...
- ThinkPHP 模板substr的截取字符串函数
ThinkPHP 模板substr的截取字符串函数在Common/function.php加上以下代码 /** ** 截取中文字符串 **/ function msubstr($str, $start ...
- java web学习总结(五) -------------------servlet开发(一)
一.Servlet简介 Servlet是sun公司提供的一门用于开发动态web资源的技术. Sun公司在其API中提供了一个servlet接口,用户若想用发一个动态web资源(即开发一个Java程序向 ...
- Spring Quartz实现任务调度
任务调度 在企业级应用中,经常会制定一些"计划任务",即在某个时间点做某件事情 核心是以时间为关注点,即在一个特定的时间点,系统执行指定的一个操作 任务调度涉及多线程并发.线程池维 ...
- WPF自定义控件第二 - 转盘按钮控件
继之前那个控件,又做了一个原理差不多的控件.这个控件主要模仿百度贴吧WP版帖子浏览界面左下角那个弹出的按钮盘.希望对大家有帮助. 这个控件和之前的也差不多,为了不让大家白看,文章最后发干货. 由于这个 ...