Javascript setTimeout存在0延迟问题
Javascript setTimeout with 0 delay issue
我注意到一个奇怪的行为:如果我有一系列任务并希望推迟它们的执行,那么我可以为每个任务使用0延迟的setTimeout。(参见http://javascript.info/tutorial/events-and-timing-depth#the-settimeout-func-0-trick)
一切都很完美:任务被排队并尽快执行。
但是。。。如果各种setTimeout的调用非常接近,那么我发现有时(很少发生!)没有按照正确的顺序执行。为什么?
从来没有人承诺他们会按照"正确"的顺序被解雇(具有相同超时的任务将按照设置为超时的顺序执行)。setTimeout
仅保证:
- 每次超时只执行一次(除非页面在此期间失效)
- 每次超时执行的时间都不早于应该执行的时间
没有关于执行命令的消息。事实上,即使实现者试图保持顺序(即使是副作用),也很可能没有足够的时间分辨率来为所有任务提供唯一的排序顺序,并且二进制堆(在这里很可能使用)也不能保持相等密钥的插入顺序。
如果您想保留延迟任务的顺序,则只应在前一个任务完成后将其排入队列。
这应该有效:
var defer = (function(){
//wrapped in IIFE to provide a scope for deferreds and wrap
var running = false;
var deferreds = [];
function wrap(func){
return function(){
func();
var next = deferreds.shift();
if(next){
setTimeout(wrap(next),0);
}else{
running = false;
}
}
}
return function(func){
if(running){
deferreds.push(func);
}else{
setTimeout(wrap(func),0);
running = true;
}
}
})()
演示:http://jsfiddle.net/x2QuB/1/
您可以考虑使用jquery defereds(或defereds的一些其他实现),它可以非常优雅地处理这种模式。
需要注意的重要一点是,延迟完成的回调是按照添加它们的顺序执行的。
var createCountFn = function(val){
return function(){
alert(val)
};
}
// tasks
var f1 = createCountFn(1),
f2 = createCountFn('2nd'),
f3 = createCountFn(3);
var dfd = $.Deferred();
dfd.done(f1).done(f2).done(f3);
dfd.resolve();
演示
HTML5草案规范规定setTimeout方法可以异步运行(这意味着回调的执行顺序可能不会被保留),这可能是浏览器正在做的事情。
setTimeout()方法必须运行以下步骤:
6.;返回句柄,然后继续异步运行此算法。
7.;如果方法上下文是Window对象,请等待,直到与方法上下文关联的Document完全活动一段超时毫秒(不一定连续)。
在任何情况下,人们都可以通过做类似的事情来解决这个问题:
function inOrderTimeout(/* func1[, func2, func3, ...funcN], timeout */)
{ var timer; // for timeout later
var args = arguments; // allow parent function arguments to be accessed by nested functions
var numToRun = args.length - 1; // number of functions passed
if (numToRun < 1) return; // damm, nothing to run
var currentFunc = 0; // index counter
var timeout = args[numToRun]; // timeout should be straight after the last function argument
(function caller(func, timeout) // name so that recursion is possible
{ if (currentFunc > numToRun - 1)
{ // last one, let's finish off
clearTimeout(timer);
return;
}
timer = setTimeout(function ()
{ func(); // calls the current function
++currentFunc; // sets the next function to be called
caller(args[currentFunc], timeout);
}, Math.floor(timeout));
}(args[currentFunc], timeout)); // pass in the timeout and the first function to run
}
- jQuery通过步骤的自排队循环来解决延迟问题
- 我在向jquery脚本添加延迟时遇到问题
- 解决延迟问题无法按预期进行
- 鼠标输入事件上的jQuery延迟出现问题
- 顶级/下级菜单上的隐藏()延迟问题,但当鼠标再次进入时取消隐藏()
- j查询周期延迟问题
- Jquery .each 延迟问题
- jQuery如何解决<脚本延迟>问题
- 延迟加载或图像加载序列问题
- 解决所有延迟问题后执行某些操作
- 使用jQuery自定义JS Confirm Modals.延迟和基于按钮的返回值问题
- Websocket通信延迟问题
- Jquery延迟问题
- jQuery Carousel插件按钮延迟问题
- 返回确认延迟问题
- 向页面添加大量元素后出现的延迟问题
- HTML5画布javascript延迟问题
- Javascript setTimeout存在0延迟问题
- 使用鼓机的设置超时解决延迟问题
- touchStart 事件和查询命令状态延迟问题