前言
关于setTimeout,我之前的文章介绍过。setTimeout我们经常用来做延迟执行,那么setTimeout(fn,0),是不是就不延迟执行了!答案肯定是否定的。这篇文章,我详细介绍一下setTimeout(fn,0)及其应用。
案例分析
for循环中有setTimeout,是我们在闭包案例中经常遇到和解决的一个问题,看一下下面这个案例
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 0);
console.log(i);
}
对于上面的这个题目中的for循环和setTimeout结合,我之前文章中好像有写过,但是记不清在哪里写的了!这个题目的打印结果是什么呢?
分析
对于这个题目,我们可以这么考虑,先把for循环里面的内容抽出来
setTimeout(function() {
console.log(i);
}, 0);
console.log(i);
对于这个,肯定是先执行 console.log(i),后执行setTimeout。那么,我们把上面的for循环拆解开来,如下:
var i = 0;
setTimeout(function() {
console.log(i);
}, 0);
console.log(i);
i++;
setTimeout(function() {
console.log(i);
}, 0);
console.log(i);
i++;
setTimeout(function() {
console.log(i);
}, 0);
console.log(i);
i++;
因为setTimeout是注册事件,要等到当前脚本的同步任务和“任务队列”中已有的事件,全部处理完以后,才能执行。因此,上面执行结果是把所有的console都执行完毕之后才能执行setTimeout,因此,执行结果如下:
0
1
2
3
3
3
for循环和setTimeout
这个我之前文章中好像有提及过,针对这种情况,我们将上面案例如下修改,就可以将setTimeout也可以输出1,2,3.
我们将setTimeout里面的匿名函数修改成自调用匿名函数
function() {
console.log(i);
}
修改成
(function(n) {
console.log(n);
})(i)
因为setTimeout的第一个参数必须是fn,因此,我们要对这个匿名函数返回一个function
(function(n) {
return function() {
console.log(n);
};
})(i)
那么,代码如下:
for (var i = 0; i < 3; i++) {
setTimeout((function(n) {
return function() {
console.log(n);
};
})(i), 0);
console.log(i);
}
这样运行结果就是
0
1
2
0
1
2
应用
我们看下面的运行结果
console.log(1);
setTimeout(function() {
console.log(2);
}, 0);
Promise.resolve().then(function() {
console.log(3);
}).then(function() {
console.log(4);
});
console.log(5);
// 1
// 5
// 3
// 4
// 2
上面代码的执行结果说明,setTimeout(fn, 0)在Promise.resolve之后执行。这是因为setTimeout语句指定的是“正常任务”,即不会在当前的Event Loop(事件循环)执行。而Promise会将它的回调函数,在状态改变后的那一轮Event Loop(事件循环)指定为微任务。所以,3和4输出在5之后、2之前。
setTimeout(fn, 0)的一大应用是,可以调整事件的发生顺序。比如,网页开发中,某个事件先发生在子元素,然后冒泡到父元素,即子元素的事件回调函数,会早于父元素的事件回调函数触发。如果,我们先让父元素的事件回调函数先发生,就要用到setTimeout(fn, 0)。
案例如下:
document.getElementById("haoroomsID").onclick = function A() {
setTimeout(function B() {
console.log("触发子元素事件")
}, 0)
};
document.body.onclick = function C() {
console.log("触发父元素事件")
};
上面案例,点击haoroomsID会先触发父级元素事件,然后再触发子元素事件。
扩展阅读
关于setTimeout(fn,0)就先写到这里,有关Event Loop(事件循环)的相关文章,请看:http://www.ruanyifeng.com/blog/2014/10/event-loop.html