为什么setTimeout(fn, 0) 会起作用?

就这个问题,我通过详细记录浏览器的工作过程并展示settimeout()是怎么起作用的。它看起来有很长但事实上非常简单和直观,我仅仅是让它更详细而已。

更新:我已经制作了一个JSFiddle的在线实例解释如下:http://jsfiddle.net/C2YBE/31/ .

------------------------

详细:

想象网页上有一个按钮为“do somthing”和一个结果区。

do somthing上绑定的一个事件方法名字叫“LongCalc”,它完成两件事:

1.做一个长时间的计算(比如3分钟)

2.打印结果到结果区。

现在,你的用户开始测试,点击“do something”按钮,但是也没好像停在那什么也没干持续了3分钟,他们着急了,再次点击按钮,等待了1分钟,还是什么也没发生,然后再次点击..

这个问题很明显,你需要一个状态的div,用于显示发生了什么。让我们看看它是怎么工作的。

----------------------

所以你增加了一个状态DIV(初始为空),并修改点击事件“LongCalc”做了4件事:

1. 将状态“Calculating...may take 3 minutes” into status DIV.

2. 执行一个长计算

3. 打印计算结果到结果区。

4. 将状态“Calculation done”放进状态的DIV.

之后,你很高兴的讲应用交给用户测试。

他们非常生气的回来了。说当他们点击按钮的时候,这个状态的DIV从来就没有更改为“Calculating”。

-------------------------

你抓狂了,在StackOverflow上提问,或者google,并意识到这个问题:

浏览器将所有的事件类的TODO任务(包括UI任务和javascipt命令)放进一个单一队列。并且很不幸的,重绘状态的有“Calculating...”字符的DIV是一个分离的TODO,而且将会放置到队列的最后。

下面是当你的用户测试的事件的统计,每一个事件的队列中的内容。

.Queue:[空]

.Event:点击按钮。队列数组:[执行onclick(lines1-4)];

.Event:执行onclick方法的第一行代码(改变状态DIV的值)。队列数组:[执行onclick的(line2-4),重绘状态DIV为“Calculating”].请注意当DOM改变是即刻的,但重绘对应的DOM元素却需要一个新的事件并触发DOM改变,这个事件就会放置到队列的后面。

.PROBLEM!!PROBLEM!!,下面将解释。

.EVENT:执行第二行代码(calulation).队列数组:[执行onclick(lines 3-4),重绘状态DIV为“Calculating”]。

.EVENT:执行第三行代码(处理result 的DIV)。队列数组:[执行onclick(lines 4),重绘状态DIV为“Calculating,重绘结果DIV]。

.EVENT:执行第四行代码(状态div显示为"DONE").队列数组:[执行onclick(lines 4),重绘状态DIV为“Calculating,重绘结果DIV,重绘状态DIV为“DONE”]。

.EVENT(假的):onclick的子函数完成。我们把onclick移除队列并开始执行下一个。

.NOTE:从我们已经完成了计算,3分钟已经过去了。重绘工作还是没有发生。

.EVENT:重绘状态DIV为“Calculating”.我们做重绘并且从队列中移除。

.EVENT:重绘结果DIV为对应的结果,我们做重绘并且从队列中移除。

.EVENT:重绘状态DIV为“DONE”.我们做重绘并且从队列中移除。

               眼尖的人很可能会看见状态DIV里一闪"Calculating",会在计算完成的时候


所以,这个潜在的问题是重绘的事件被放到了队列的最后,在执行line2事件的时候花费了3分钟,所以真正的重绘直到计算完成才会执行。

------------------------

解决的办法是setTimeout().是怎么起作用的呢?因为通过settimeout调用长执行代码,你实际上创建了2个events:settimeout执行它自己,之后0timeout后,长代码将被执行。

所以,解决你的问题,修改onclick为两块。

1.放置状态"Calculating...may take 3 minutes" 到状态DIV

2. 执行包含“LongCalc”方法的0延迟的settimout。(LongCalc function is almost the same as last time but obviously doesn't have "Calculating..." Status DIV update as first step, and instead starts the calculation right away.)

所以,那现在event的顺序和这个队列的是个什么样子?

.Queue:[empty].

.Event:点击按钮:队列数组:[执行onclick(状态更新,调用setimeout)].

.Event:执行第一行(改变状态Div的值)。队列数组:[执行onclick(调用setimeout),重绘 状态 DIV为"Calculating"值]。

.Event:执行第二行代码(setTimeout),队列数组:[重绘 状态 DIV为"Calculating"值].队列将不会有新东西在0秒内。

.Event:0秒后,时钟将停止。队列数组:[重绘 状态 DIV为"Calculating"值],执行LongCalc(line 1-3)].

.Event:重绘状态区"Calculating".队列数组:[执行LongCalc(line 1-3)].

请注意这次重绘可能事实上在时钟停止前,重绘也能正常工作。


好了。在计算开始前,状态DIV将更新为"Calculating..."



原文地址:http://stackoverflow.com/questions/779379/why-is-settimeoutfn-0-sometimes-useful

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值