工作中经常看到一些代码,使用setTimeout延时0毫秒后执行一些功能函数。这一点令人疑惑,延时0毫秒再执行,不是应该和直接执行代码是同样的效果吗?为什么我们还要使用setTimeout,并设置0毫秒的延时呢?这难道不是多此一举吗?
实则不然。
我们知道,JavaScript的执行是单线程的,而且是按照顺序一条条执行的,除非遇到异步操作,比如Ajax。但是,有种情况比较特殊,就是被setTimeout包裹的代码,它们的执行实际上是异步的,比如以下代码:
alert('1.visit www.4455q.com');
setTimeout(function(){
alert('3.leave it');
}, 0);
alert('2.mark it');
1
2
3
4
5
alert('1.visit www.4455q.com');
setTimeout(function(){
alert('3.leave it');
},0);
alert('2.mark it');
setTimeout包裹的代码,总是最后执行的,通俗地理解,就是setTimeout实现了异步的操作。
那么,这种特性,有什么具体作用呢?比如,
填写域名
1
2
填写域名
我希望触发#btn按钮的mousedown事件的时候,给input[name=domain]填入4455q.com,同时要获取焦点,并且选中,那么,我可能会这样实现(为了方便演示,假设已经引入jquery):
// 代码段1
$('#btn').on('mousedown', function(e){
$('input[name=domain]').val('4455q.com').focus().select();
})
1
2
3
4
// 代码段1
$('#btn').on('mousedown',function(e){
$('input[name=domain]').val('4455q.com').focus().select();
})
然而,实际上,这段代码的执行结果与我预期的有所不同,虽然,值填入了input,但是,并没有获取焦点和选中。如果用setTimeout将代码进行包裹,就实现了我们想要的效果。
// 代码段2
$('#btn').on('mousedown', function(e){
setTimeout(function(){
$('input[name=domain]').val('4455q.com').focus().select();
}, 0);
})
1
2
3
4
5
6
// 代码段2
$('#btn').on('mousedown',function(e){
setTimeout(function(){
$('input[name=domain]').val('4455q.com').focus().select();
},0);
})
问题虽然解决了,但是原因是什么呢?
先看前面的代码段1
显然,在mousedown鼠标按下的时候,代码的执行顺序如下
1.给input[name=address]赋值
2.input[name=address]获得焦点
3.input[name=address]内容被选中
4.最关键的一步:#btn按钮获得焦点(mousedown事件之后,其实会触发focus事件)。
也就是说,第4步的获取焦点,使得前面2,3两步的执行都无效了。
那么,问题就很清楚了,我们如果能够把2,3两步的执行放到第4步的后面,那么,就能很好地实现这个功能了。
前面我们已经提到,setTimeout包裹的代码会延迟执行,可以看成是异步操作。这就是使用setTimeout解决这一问题的原因了,其实就是利用它的特性来重新安排代码的执行顺序。