关于SetTimeout 时间设为0时。

问题来源: http://blog.csdn.net/lsk_jd/article/details/6080772

这是一个关于Settimeout时间为0时的一些技巧应用,不过看完后觉得有些问题,
里面的例子,如果onmousedown 改为onmouseup,onkeypress 改为 onkeyup那文章里说的问题都没有了。不知道这是为啥?
onmousedown的运作方式又是如何的,或者说文章里那个onmousedownfocus,select不执行是因何,说是单线程的问题,但具体是啥。

阅读 23.3k
5 个回答

依云回答的是正确的。而我在上周因为你这个问题,而编写了一篇文章:
JavaScript下的setTimeout(fn,0)意味着什么?

你所读的那篇文章讲解的核心是正确的,但是原作者使用了错误的方法来给你演示:onmousedown
你被作者的onmousedown给误解了,因为Event onmouseXXX系列API本身相对其他的事件来说稍显复杂。
其中,作者演示了代码onmousedown系的代码,在未使用setTimeout的情况下,input.select()看似没有执行。
但真实的情况是:input.select()执行了,然后被按钮点击的焦点给覆盖了。
原文作者的初衷是好的,但其演示的代码误导了自己和读者,并以此得到了一段并不正确的理论。
至于详细的分析请参阅上面我给出的我的文章。

是的,浏览器跑网页里的 JS 是单线程,setTimeout 和直接写的执行顺序会不一样。但是,CSDN 和博客园既不是 W3C 也不是 MDN,出点误导苍生的文章很正常。连 YouTube 上都一堆视频以「优化」的名义教人劣化呢1。文章可能没说错任何一句话,但是现象和解释是不对应的。

如果onmousedown 改为onmouseup,onkeypress 改为 onkeyup那文章里说的问题都没有了。不知道这是为啥?

这是因为执行顺序的关系。setTimeout 也是因为改变了执行顺序所以会有效果。

那个选不中 input 内的文本的原因是,在 select 事件之后,又发生了 click 事件(在 mouseup 事件之后),click 事件导致了 DOMActivate 事件,这导致焦点被立即移回按钮,input 被 blur。如果你在这个生成的 input 上监听 select 和 blur 事件的话,可以看到你的事件处理函数也会同步被执行的。不是不执行,而是执行的效果被取消了

当然,实测在火狐 36.0.1 下,再次 focus 这个 input 时里边的文本首先会被选中然后取消。经过测试,我认为这是浏览器渲染的瑕疵,并没有相应的事件发生。

@justjavac 回答里提到的文章(因为 markdown 不支持 rel="nofollow" 所以我不作链接了)中第二组例子,字符显示会有「延迟」,原因很简单,处理 keypress 事件时 input 的 value 还没有被改变。等到 input 事件发生时就好。你在 input 事件发生时处理的话就没有这种「延迟」。

如果你们没有博客园的帐号,没法轻松地复制出里边的代码,就学一个像 Vim 这样功能强大的编辑器,自己做实验试试。

以上操作全部在火狐 36.0.1 Linux 64 版本上实验的。感谢 Firebug 的「记录事件」功能,大大简化了这类现象的原因调查流程。

(本答案仅供参考,我也是看的博客园的二手资料,把赞都给 @依云 吧)


JavaScript 是单线程执行的,也就是无法同时执行多段代码,当某一段代码正在执行的时候,所有后续的任务都必须等待,形成一个队列,一旦当前任务执行完毕,再从队列中取出下一个任务。这也常被称为 “阻塞式执行”。

假如当前 JavaScript 进程正在执行一段很耗时的代码,此时发生了一次鼠标点击,那么事件处理程序就被阻塞,用户也无法立即看到反馈,事件处理程序会被放入任务队列,直到前面的代码结束以后才会开始执行。

如果代码中设定了一个 setTimeout,那么浏览器便会在合适的时间,将代码插入任务队列,如果这个时间设为 0,就代表立即插入队列,但不是立即执行,仍然要等待前面代码执行完毕。所以 setTimeout 并不能保证执行的时间,是否及时执行取决于 JavaScript 线程是拥挤还是空闲。

对于实际代码的分析,看看这篇文章:http://www.cnblogs.com/fullhouse/archive/2012/10/10/2718542.html

简单点说就是 JS 单线程,执行顺序先 DOM(程序上的) 后渲染(即将页面的表现改变)。所以可能代码中已经将某个 <div> 里的文本改变了,但真正的渲染还没开始,而是排在程序中的最后面。

setTimeout(callback,0) 则将 callback 函数放在了队列中比渲染还后面来执行。

这是涉及到浏览器引擎的一个 event loop 机制 event loop 是一个回调函数的队列 当你对setTimeout 或者 对DOM 节点进行操作的时候 浏览器引擎会把他交给 wep api, wep api操作完之后 会把你的回调函数放进 event loop 里面 当你的调用栈 (call stack) 为空的时候 event loop 才会把回调函数顶上去调用栈里调用运行

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏