计时器setTimeout是我们经常会用到的,它用于在指定的毫秒数后调用函数或计算表达式。
语法:setTimeout(code, millisec, args);
注意:如果code为字符串,相当于执行eval()方法来执行code。
当然,这一篇文章并不仅仅告诉你怎么用setTimeout,而且理解其是如何执行的。
先来看一段代码:
var start = new Date();
var end = 0;
setTimeout(function() { console.log(new Date() - start); }, 500);
while (new Date() - start <= 1000) {}
在上面的代码中,定义了一个setTimeout定时器,延时时间是500毫秒。
你是不是觉得打印结果是: 500
可事实却是出乎你的意料,打印结果是这样的(也许你打印出来会不一样,但肯定会大于1000毫秒):1004
这是为毛呢?究其原因,这是因为 JavaScript是单线程执行的。也就是说,在任何时间点,有且只有一个线程在运行JavaScript程序,无法同一时候运行多段代码。
再来看看浏览器下的JavaScript。
浏览器的内核是多线程的,它们在内核控制下相互配合以保持同步,一个浏览器至少实现三个常驻线程:JavaScript引擎线程,GUI渲染线程,浏览器事件触发线程。
到这里,我们再来回顾一下最初的例子:
var start = new Date();
var end = 0;
setTimeout(function() { console.log(new Date() - start); }, 500);
while (new Date() - start <= 1000) {}
虽然setTimeout的延时时间是500毫秒,可是由于while循环的存在,只有当间隔时间大于1000毫秒时,才会跳出while循环,也就是说,在1000毫秒之前,while循环都在占据着JavaScript线程。也就是说,只有等待跳出while后,线程才会空闲下来,才会去执行之前定义的setTimeout。
最后 ,我们可以总结出,setTimeout只能保证在指定的时间后将任务(需要执行的函数)插入任务队列中等候,但是不保证这个任务在什么时候执行。一旦执行javascript的线程空闲出来,自行从队列中取出任务然后执行它。
因为javascript线程并没有因为什么耗时操作而阻塞,所以可以很快地取出排队队列中的任务然后执行它,也是这种队列机制,给我们制造一个异步执行的假象。
也许你见过下面这一段代码:
setTimeout(function(){ // statement}, 0);
上面的代码表示立即执行。本意是立刻执行调用函数,但事实上,上面的代码并不是立即执行的,这是因为setTimeout有一个最小执行时间,当指定的时间小于该时间时,浏览器会用最小允许的时间作为setTimeout的时间间隔,也就是说即使我们把setTimeout的延迟时间设置为0,被调用的程序也没有马上启动。
不同的浏览器实际情况不同,IE8和更早的IE的时间精确度是15.6ms。不过,随着html5的出现,在高级版本的浏览器(Chrome、ie9+等),定义的最小时间间隔是不得低于4毫秒,如果低于这个值,就会自动增加,并且在2010年及之后发布的浏览器中采取一致。
所以说,当我们写为 setTimeout(fn,0) 的时候,实际是实现插队操作,要求浏览器“尽可能快”的进行回调,但是实际能多快就完全取决于浏览器了。
那setTimeout(fn, 0)有什么用处呢?其实用处就在于我们可以改变任务的执行顺序!因为浏览器会在执行完当前任务队列中的任务,再执行setTimeout队列中积累的的任务。
通过设置任务在延迟到0s后执行,就能改变任务执行的先后顺序,延迟该任务发生,使之异步执行。
来看一个网上很流行的例子:
document.querySelector('#one input').onkeydown = function() {
document.querySelector('#one span').innerHTML = this.value;
};
document.querySelector('#second input').onkeydown = function() {
setTimeout(function() {
document.querySelector('#second span').innerHTML = document.querySelector('#second input').value; }, 0);
};
当你往两个表单输入内容时,你会发现未使用setTimeout函数的只会获取到输入前的内容,而使用setTimeout函数的则会获取到输入的内容。
这是为什么呢?
因为当按下按键的时候,JavaScript 引擎需要执行 keydown 的事件处理程序,然后更新文本框的 value 值,这两个任务也需要按顺序来,事件处理程序执行时,更新 value值(是在keypress后)的任务则进入队列等待,所以我们在 keydown 的事件处理程序里是无法得到更新后的value的,而利用 setTimeout(fn, 0),我们把取 value 的操作放入队列,放在更新 value 值以后,这样便可获取出文本框的值。
未使用setTimeout函数,执行顺序是:`onkeydown => onkeypress => onkeyup
使用setTimeout函数,执行顺序是:onkeydown => onkeypress => function => onkeyup`
虽然我们可以使用keyup来替代keydown,不过有一些问题,那就是长按时,keyup并不会触发。
长按时,keydown、keypress、keyup的调用顺序:
keydown
keypress
keydown
keypress
...
keyup
也就是说keyup只会触发一次,所以你无法用keyup来实时获取值。
我们还可以用setImmediate()来替代setTimeout(fn,0):
if (!window.setImmediate) {
window.setImmediate = function(func, args){
return window.setTimeout(func, 0, args);
};
window.clearImmediate = window.clearTimeout;
}
setImmediate()`方法用来把一些需要长时间运行的操作放在一个回调函数里,在浏览器完成后面的其他语句后,就立刻执行这个回调函数,必选的第一个参数func,表示将要执行的回调函数,它并不需要时间参数。
注意:目前只有IE10支持此方法,当然,在Nodejs中也可以调用此方法。
3.1 setTimeout中回调函数的this
由于setTimeout() 方法是浏览器 window 对象提供的,因此第一个参数函数中的this其实是指向window对象,这跟变量的作用域有关。
看个例子:
var a = 1;
var obj = {
a: 2,
test: function() { setTimeout(function(){ console.log(this.a); }, 0);
}
};
obj.test(); // 1
不过我们可以通过使用bind()方法来改变setTimeout回调函数里的this
var a = 1;
var obj = {
a: 2,
test: function() {
setTimeout(function(){
console.log(this.a);
}.bind(this), 0);
}
};
obj.test(); // 2
3.2 setTimeout不止两个参数
我们都知道,setTimeout的第一个参数是要执行的回调函数,第二个参数是延迟时间(如果省略,会由浏览器自动设置。在IE,FireFox中,第一次配可能给个很大的数字,100ms上下,往后会缩小到最小时间间隔,Safari,chrome,opera则多为10ms上下。)
其实,setTimeout可以传入第三个参数、第四个参数….,它们表示神马呢?其实是用来表示第一个参数(回调函数)传入的参数。
setTimeout(function(a, b){
console.log(a); // 3
console.log(b); // 4},0, 3, 4);
惊为天人的发现promise里面的一个例子:原来 setTimeout居然还有第三个参数,调用方法的时候可以作为传参对象。定时器启动时,第三个及以后的参数是作为第一个参数(也就是函数)的参数传进去的。
这段JavaScript代码当你在页面打开2s时间内点击一次按钮,效果是这样的:页面卡住大约5s钟大约5s后弹出 ,click handler点击弹窗确认后,弹出 timer handler
今天要说的很简单,没有 setTimeout 的基本用法,也没有什么特殊用法。就是想记录一下 setTimeout 的一个特殊情况,分享给可能也不知道的你们。其中第二个参数是需要延时执行的毫秒数,大家应该都知道这个时间是不准确的,可以理解为最短延时。
在js中setTimeout和setInterval都是用来定时的一个功能,下面这篇文章主要给介绍了JS中setInterval和setTImeout的this指向问题,文中通过示例介绍的很详细,有需要的朋友可以参考借鉴,一起来看看吧。
说起来你可能不相信,setTimeout居然有第三个参数,我以前也没用过这个,是前几天看别人博客发现的,咋一看还以为写错了吧,下面一起看看这个setTimeout第三个参数。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!