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

来源:互联网 发布:cf英雄武器抽奖 软件 编辑:程序博客网 时间:2024/06/05 21:18

就这个问题,我通过详细记录浏览器的工作过程并展示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

0 0
原创粉丝点击