在写js代码过程中,超时调用 setTimeout
与 间隔调用 setInterval
方法会经常用到
setTimeout
基本使用
JavaScript是一个单线程的解释器,因此一段时间内只能执行一段代码,为了要控制执行的代码,就会有一个JavaScript的任务队列,这些任务会按照他们添加到队列的顺序执行。 setTimeout
的第二个参数会告诉JavaScript再过多长时间把当前任务添加进队列,如果队列是空的,那么添加 的代码会立即执行,如果队列不是空的,那么它要等前面的代码执行完了再执行。
//avoid!
setTimeout("alert('Hello world!') ", 1000);
//preferred
setTimeout(function() {
alert("Hello world!");
}, 1000);
setTimeout(() => {
alert("haha");
}, 5000);
setTimeout(test, 10000);
function test() {
alert('test');
}
以上代码可以帮助很好地理解JavaScript的任务队列这一功能,打开网页,我们等待10秒,会发现第一个 setTimeout 任务已经执行了,点击确认后,后续的弹窗都会出现,因为所有的 setTimeout 都已经到超时时间, 进入任务队列排队了。
几个注意点
this 的问题
当执行 setTimeout 中的回调函数时,它的执行环境(execution context)与调用 setTimeout 的执行环境不同,由此会引起函数调用与预期不符的问题,可以看MDN上的示例代码。
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, 1.0*1000); // prints "[object Window]" after 1 second
setTimeout(myArray.myMethod, 1.5*1000, '1'); // prints "undefined" after 1.5 seconds
可以发现,上述两部分代码运行结果不一致,因为 setTimeout
在调用myArray.myMethod
时没有设置它的 this
, 所以这时候 this 就默认是 window
,解决上述问题有两种方法,第一种是再包一层方法,第二种是直接用 bind
setTimeout(function(){myArray.myMethod()}, 2.0*1000); // prints "zero,one,two" after 2 seconds
setTimeout(function(){myArray.myMethod('1')}, 2.5*1000); // prints "one" after 2.5 seconds
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, 1.0*1000); // still prints "zero,one,two" after 1 second because of the binding
setTimeout(myBoundMethod, 1.5*1000, "1"); // prints "one" after 1.5 seconds
setInterval
setInterval
用法和setTimeout
很相似,这里列下基本代码实例。
var num = 0;
var max = 10;
var intervalId = null;
function incrementNumber() {
num++;
//if the max has been reached, cancel all pending executions
if (num == max) {
clearInterval(intervalId);
console.log("Done");
} else {
console.log('not yet');
}
}
intervalId = setInterval(incrementNumber, 500);
需要注意,在开发模式下很少使用真正的间歇调用,原因是后一个间歇调用可能会在前一个间歇调用结束之前启动,推荐使用超时调用代替。
var num = 0;
var max = 100;
function incrementNumber() {
num++;
//if the max has not been reached, set another timeout
if (num < max) {
setTimeout(incrementNumber, 500);
} else {
var num = 0;
var max = 10;
function incrementNumber() {
num++;
//if the max has not been reached, set another timeout
if (num < max) {
console.log('not yet');
setTimeout(incrementNumber, 500);
} else {
console.log('not yet');
}
}
setTimeout(incrementNumber, 500);
}
}
setTimeout(incrementNumber, 500);
参考
JavaScript高级程序设计(第三版)
WindowOrWorkerGlobalScope.setTimeout()
Function.prototype.bind()
this