下面代码的输出结果:
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
答案是:10个10
由此,我们来了解一下JavaScript的定时机制。
众所周知,JavaScript引擎是单线程的,我们先来看一下JavaScript的线程图示:
如图,一般浏览器内核中至少常驻三个线程:JavaScript引擎线程、GUI渲染线程、浏览器事件触发线程等。
其他线程产生的任务放到JavaScript引擎线程的队列中,然后一个接一个的执行。
//代码一
setTimeout(function() {
console.log("123");
}, 1000);
//代码二
setTimeout(function() {
console.log("abc");
setTimeout(arguments.callee, 1000);
}, 1000);
//代码三
setInterval(function(){
console.log("xyz");
}, 1000);
代码一,延迟执行,一秒后控制台打印出123
代码三,循环执行,每过一秒(<=1s)打印xyz
代码二,也是循环执行,每过一秒(>=1s)打印abc
由于arguments.callee不推荐使用了,所以代码二可以等价的写成:
setTimeout(function a() {
console.log("abc");
setTimeout(a, 1000);
}, 1000);
代码二和代码三是循环执行,会一直执行下去,容易造成浏览器假死,没反应,如果想要结束循环,可以通过如下方式:
//代码二改成这样,只执行10次
var num=0;
setTimeout(function a() {
num++;
console.log("abc");
if(num<10){
setTimeout(a, 1000);
}
}, 1000);
//代码二也可以改成这样,执行11次
var num=0;
setTimeout(function a() {
num++;
console.log("abc");
var timer=setTimeout(a, 1000);
if(num>10){
clearTimeout(timer);
}
}, 1000);
同样的道理,代码三可以改成如下写法:
//9次
var num=0;
setInterval(function(){
num++;
if(num<10){
console.log("xyz");
}
}, 1000);
//11次
var num=0;
var i=setInterval(function(){
num++;
console.log("xyz");
if(num>10){
clearInterval(i);
}
}, 1000);
差不多就这样吧,总结一下:
- JavaScript是单线程的
- JavaScript引擎是基于事件驱动的
- 定时器产生的异步事件会插入到JavaScript引擎的事件队列中
- JavaScript引擎执行完同步事件后会去继续执行事件队列中的异步事件,这就是为什么如下代码先打印0再打印1的原因
setTimeout(function(){
console.log(1);
},0);
console.log(0);
- 两个setTimeout构成的循环可以使用clearTimeout()去结束循环
- setInterval循环可以使用clearInterval()去结束循环
参考阅读:
深入理解JavaScript定时机制
深入理解定时器系列第一篇——理解setTimeout和setInterval
浏览器UI多线程及对JavaScript单线程底层运行机制的理解