32

考虑以下 Javascript 代码:

function(){
    setTimeout(function() {
        $("#output").append(" one ");
    }, 1000);
    setTimeout(function() {
        $("#output").append(" two ");
    }, 1000);
}

您还可以在jsfiddle上查看此示例。

我可以确定的值#outputalways "one two",按那个顺序吗?通常,我会这样处理这个问题:

function(){
    setTimeout(function() {
        $("#output").append(" one ");
        $("#output").append(" two ");
    }, 1000));
}

但我不能那样做,因为我从服务器获取消息,告诉我要执行哪个函数(在本例中为 append"one"或 append "two"),我必须稍微延迟执行。

我已经在 Internet Explorer 9、Firefox 14、Chrome 20 和 Opera 12 中测试过这段代码,并且输出总是"one two",但我可以确定这将永远是这种情况吗?

4

9 回答 9

15

规范在这里

我对第 7.3 节中第 8 步的解释应该保证setTimeout执行顺序。

但是,我调查了这个问题,因为当窗口在 Chrome 中最小化然后最大化时,我发现来自外部源(如 websockets 或 webworkers)的事件中设置的超时以错误的顺序执行。我认为这是一个浏览器错误,并有望很快得到修复。

于 2013-01-25T20:21:25.633 回答
4

在你的小提琴中玩这个

$(document).ready(function() {
    setTimeout(function() {
        $("#output").append(" one ");
    }, 1000);
});
$(document).ready(function() {
    setTimeout(function() {
        $("#output").append(" two ");
    }, 999);
});​

你会看到两者

output: one two
output: two one

是可能的。所以 Speransky 是对的,你不能总是依赖于你的超时以相同的顺序执行。

请注意,我用 1ms 更改了一次,以表明超时 1000ms 可以在 999ms 超时之前执行。

编辑:下面的代码可能会延迟执行,而two之前没有任何机会打印one

function(){
    setTimeout(function() {
        $("#output").append(" one ");
       setTimeout(function() {
           $("#output").append(" two ");
       }, 100);
    }, 1000);
}
于 2012-08-02T05:47:09.943 回答
2

不,你不能确定。它是异步的。

但实际上它可能会是真的,因为在浏览器中实现了该机制。

于 2012-08-02T05:33:38.143 回答
2

是的,因为 javascript 代码是在一个线程中执行的,所以所有异步事件,如click, mousemove,都会排队等待执行。当您调用setTimeout时,引擎会在其队列中插入一个计时器以在将来执行,至少在某个delay时间之后执行。所以两者setTimeout生成了两个定时器,一个接一个。

你可以看看John Resig 的How Javascript Timers Work

于 2012-08-02T05:45:45.017 回答
0

是的,输出将始终为"one two".

因为第一个setTimeout总是在第二个之前运行setTimeout,如果它们有相同的延迟时间,那么第一个的回调函数总是在第二个的回调函数之前执行。

于 2012-08-02T05:36:32.807 回答
0

更新
我已经用这种方法更新了你的小提琴。在这里查看

这个方法怎么样——从你的服务器获取要做的事情列表——

//Example - append one, append two
var appendList = ['one', 'two']

//then do this
appendList.forEach(function(item, index){
    setTimeout(function(){ 
        $('#output').append(item)
    }, index * 50 + 1000);
});

这样,您就可以决定顺序。

于 2012-08-02T06:55:14.923 回答
0

如果事件是同步的,则有Continuum按顺序运行函数的功能:

function keyedSequence(key, fn) {
  fn = fn || this;
  key.push({fn:fn});

  return function() {
    for(var i=0, n, full=1; i<key.length; i++) {
      n = key[i];
      if(n.fn == fn) {
        if(!n.called) n.called = 1, n.args = key.slice.call(arguments);
        if(!full) break
      }
      if(!n.called) full = 0
    }

    if(full) for(i=0; i<key.length; i++)
      n = key[i], key[i] = {fn:n.fn}, n.fn.apply(n, n.args);
  }
}
Function.prototype.seq = keyedSequence;

您提供一个空数组作为键。使用相同键键入的功能将组合在一起。

window.onload = function() {
  var key = [];
  document.getElementById("test1").addEventListener('click', function1.seq(key), false);
  document.getElementById("test2").addEventListener('click', function2.seq(key), false);
}

单击test2,然后单击test1,执行顺序仍然是function1then function2

另一种调用方式是:

window.onload = function() {
  var key = [];
  document.getElementById("test1").addEventListener('click', keyedSequence(key, function1), false);
  document.getElementById("test2").addEventListener('click', keyedSequence(key, function2), false);
}
于 2012-09-13T13:48:17.723 回答
0

这取决于浏览器,因为 setTimout 是浏览器中窗口对象的一部分,而不是在 ECMAScript 中定义。

于 2018-07-10T23:17:19.160 回答
0

根据规范,具有相同的任务源,并且应该订购具有相同任务源的东西。所以是的。

于 2020-06-17T00:13:19.867 回答