在开发过程中,我们比较常使用setTimeout做一些动画或者定时任务,但通常时间参数都是大于0的。但我们在前端笔试的过程中,经常会遇到setTimeout(function(){}, 0)的问题,如果你对事件循环event loop不熟悉的话,就容易做错。
关于此类问题,我们先看一个有趣的案例:
setTimeout延迟0秒 (function(){ var oInput = document.getElementById("input"); var oSpan = document.getElementById("preview"); oInput.onkeypress = function(){ oSpan.innerHTML = this.value; } })();
这个onkeypress事件的本意是想要监听用户输入字符串的同时展现出来,但诡异的是最后一个字符总是不能显示出来。(注意onkeypress必须要在输入法英文状态下才能触发。)
如下图:
那么如何解决这个问题呢?没错,就是用setTimeout时间参数延迟为0即可。
oInput.onkeypress = function(){ setTimeout(function(){ oSpan.innerHTML = oInput.value; },0); }
setTimeout()是用来改变任务执行顺序的,用来延迟执行的异步函数。
目的是将onkeypress事件延迟到浏览器更新相关Dom元素的状态之后执行,这时就能显示完所有字符串了。如下图:
除了这个办法,有其他的替代方法吗?当然是有的。
就是把onkeypress事件改为onkeyup事件,一样能达到同样效果:
oInput.onkeyup = function(){ oSpan.innerHTML = this.value;}
之所以能达到这样的效果,主要跟key事件的执行顺序有关:
oInput.onkeypress = function(){ console.log("onkeypress");} oInput.onkeyup = function(){ console.log("onkeyup");} oInput.onkeydown = function(){ console.log("onkeydown");}
键盘输入1后,控制台打印结果如下:
由此可见,key事件的执行顺序为,先执行keydown,再执行keypress事件,执行keypress事件之后改变Dom元素的状态(这里是input的value改变了),
再执行keyup。
这也解释了为什么通过监听keyup事件就能正确且及时的打印出input的值。
而keypress事件发生时,dom元素的状态还未改变,keypress事件之后dom元素的状态才发生改变,通过setTimeout延迟执行就能达到期望的结果了。
测试过chrome,firefox,ie表现一致。
总结:
setTimeout确实是实现异步执行的利器,可用来改变任务执行顺序,延迟该任务发生,使之异步执行。
认识了key事件的执行顺序,首先执行keydown,再执行keypress事件,最后执行keyup。