一、SetTimeout与ClearTimeout
setTimeout()方法设置一个定时器,该定时器在定时器到期后执行一个函数或指定的一段代码。每次定时器到期函数只执行一次。
句法
var timeoutID = scope.setTimeout(function[, delay, param1, param2, ...]);
var timeoutID = scope.setTimeout(function[, delay]);
var timeoutID = scope.setTimeout(code[, delay]);
参数
function
function是你想要在到期时间(delay
毫秒)之后执行的。
code
这是一个可选语法,你可以使用字符串而不是function在delay
毫秒之后编译和执行字符串 (使用该语法是不推荐的, 原因和使用 [eval()
]一样。eval() 函数会将传入的字符串当做 JavaScript 代码进行执行,是有安全风险)。
delay
延迟的毫秒数 (一秒等于1000毫秒),函数的调用会在该延迟之后发生。如果省略该参数,delay取默认值0,意味着“马上”执行,或者尽快执行。不管是哪种情况,实际的延迟时间可能会比期待的(delay毫秒数) 值长。
param1, ..., paramN
附加参数,一旦定时器到期,它们会作为参数传递给[function
]。
返回值
返回值timeoutID
是一个正整数,表示定时器的编号。这个值可以传递给clearTimeout
来取消该定时器。
需要注意的是setTimeout()
和setInterval()
共用一个编号池。技术上clearTimeout
和 clearInterval()
可以互换。但是,为了避免混淆,不要混用取消定时函数。
因此在同一个对象上(一个window或者worker),setTimeout()
或者setInterval()
在后续的调用不会重用同一个定时器编号。但是不同的对象使用独立的编号池。
例子
var myVar;
function myFunction() {
myVar = setTimeout(function(){
alert("Hello")
}, 3000);
}
function myStopFunction() {
clearTimeout(myVar);
}
上述myFunction函数3秒后alert警告信息,myStopFunction清除定时器。
setTimeout除了做定时器外还能干什么用?
非常多,比如说:在处理DOM点击事件的时候通常会产生冒泡,正常情况下首先触发的是子元素的handler,再触发父元素的handler,如果我想让父元素的handler先于子元素的handler执行应该怎么办?那就用setTimeout延迟子元素handler若干个毫秒执行吧。问题是这个“若干个”毫秒应该是多少?可以是0。请看:
(function () {
setTimeout(function () {
alert(2);
}, 0);
alert(1);
})()
会先弹出1,再弹出2。
setTimeout,setInterval都存在一个最小延迟的问题,虽然你给的delay值为0,但是浏览器执行的是自己的最小值。HTML5标准是4ms,但并不意味着所有浏览器都会遵循这个标准,包括手机浏览器在内,这个最小值既有可能小于4ms也有可能大于4ms。在标准中,如果在setTimeout中嵌套一个setTimeout, 那么嵌套的setTimeout的最小延迟为10ms。
不光是上述,其他任何你想要延迟执行的函数都可以使用SetTimeout()
。
关于"this"的问题
查看下面的例子:
let myArray = ["zero", "one", "two"];
myArray.myMethod = function (sProperty) {
alert(arguments.length > 0 ? this[sProperty] : this);
};
myArray.myMethod(); // prints "zero,one,two"
myArray.myMethod(1); // prints "one"
setTimeout(myArray.myMethod, 1000); // prints "[object Window]" after 1 second
setTimeout(myArray.myMethod, 1500, "1"); // prints "undefined" after 1.5 seconds
后两行myArray.myMethod函数传递给 setTimeout,到了定时时间,this没有指向,默认指向window对象。并且没有方法把 thisArg 传递给setTimeout。
可能的解决方案:
一个通用的方法是使用包装函数
setTimeout(function(){myArray.myMethod()}, 2000); // prints "zero,one,two" after 2 seconds
setTimeout(function(){myArray.myMethod('1')}, 2500); // prints "one" after 2.5 seconds
或者箭头函数
setTimeout(() => {myArray.myMethod()}, 2000); // prints "zero,one,two" after 2 seconds
setTimeout(() => {myArray.myMethod('1')}, 2500); // prints "one" after 2.5 second
**注:JavaScript 1.8.5 引入了 Function.prototype.bind()
方法,该方法允许显式地指定函数调用时 this 所指向的值 。该方法可以帮助你解决 this 指向不确定的问题。
let myArray = ['zero', 'one', 'two'];
myBoundMethod = (function (sProperty) {
console.log(arguments.length > 0 ? this[sProperty] : this);
}).bind(myArray);
myBoundMethod(); // prints "zero,one,two" because 'this' is bound to myArray in the function
myBoundMethod(1); // prints "one"
setTimeout(myBoundMethod, 1000); // still prints "zero,one,two" after 1 second because of the binding
setTimeout(myBoundMethod, 1500, "1"); // prints "one" after 1.5 seconds
二、SetInterval()
句法
intervalID = window.setInterval(function,delay [,param1,param2,...]);
intervalID = window.setInterval(code,delay);
-
intervalID
是定时器编号, -
function
是必须重复调用的[函数] -
code
在替代语法中,是表示要重复执行的代码的字符串。 -
delay
是setInterval()
每次调用前必须等待的毫秒数(千分之一秒)。如果delay
小于10,则使用的值将减小为10。
实例:
var y=0;
var x = new Date().getTime();
var d=setInterval(a,500);
function a() {
y++;
sleep(1000);
if(y>=4){
clearInterval(d)
}
console.log(new Date().getTime()-x);
}
function sleep(sleepTime){
var start=new Date().getTime();
while(true){
if(new Date().getTime()-start>sleepTime){
break;
}
}
}
上述控制台输出为:
可以看出,setInterval()
函数并没有每隔500ms运行一次sleep函数。因此使用setInterval()
函数是有风险的。
这里介绍一下,JS是一个单线程的解释器,因此一段时间内只能执行一段代码。为了要控制执行的代码,就有一个JS任务队列。这些任务会按照将它们添加到队列的顺序执行。如果函数执行时间过长,setInterval()
函数间隔时间过短,就会导致一些【函数】被忽略掉。
一般来说,使用超时调用是模拟(可以使用递归,即自己调用自己)间歇调用的一种最佳模式,在开发环境下很少用间歇调用。
参考:https://developer.mozilla.org/zhCN/docs/Web/API/Window/setTimeout
https://www.jianshu.com/p/fc9a08ca2c92