Порядок выполнения нескольких функций setTimeout() с одинаковым интервалом
Рассмотрим следующий код Javascript:
function(){
setTimeout(function() {
$("#output").append(" one ");
}, 1000);
setTimeout(function() {
$("#output").append(" two ");
}, 1000);
}
Вы также можете увидеть этот пример на jsfiddle.
Могу ли я быть уверенным, что значение #output
всегда "one two"
, в этом порядке? Обычно я бы справился с этой проблемой следующим образом:
function(){
setTimeout(function() {
$("#output").append(" one ");
$("#output").append(" two ");
}, 1000));
}
Но я не могу так поступать, потому что получаю сообщения с сервера, который сообщает мне, какую функцию выполнять (в этом примере добавить "one"
или append "two"
), который я должен выполнить с небольшой задержкой.
Я уже тестировал этот код в Internet Explorer 9, Firefox 14, Chrome 20 и Opera 12, а вывод всегда был "one two"
, но могу ли я быть уверенным, что это всегда будет?
Ответы
Ответ 1
Спецификация здесь.
Моя интерпретация шага 8 setTimeout
в разделе 7.3 заключается в том, что предполагается выполнение порядка выполнения.
Однако я исследовал эту проблему, потому что, когда окно минимизировано и затем максимизировано в Chrome, я обнаружил, что таймауты, установленные в событиях, поступающих из внешних источников (например, веб-узлов или веб-мастеров), выполнялись в неправильном порядке. Я предполагаю, что это ошибка браузера и, надеюсь, скоро будет исправлена.
Ответ 2
Играйте с этим в своей скрипке
$(document).ready(function() {
setTimeout(function() {
$("#output").append(" one ");
}, 1000);
});
$(document).ready(function() {
setTimeout(function() {
$("#output").append(" two ");
}, 999);
});
И вы увидите, что оба
output: one two
output: two one
возможны. Так что Сперанский прав, что вы не можете всегда полагаться на свои тайм-ауты в том же порядке.
Обратите внимание, что я меняю один раз на 1 мс, чтобы показать, что тайм-аут 1000 мс может выполняться до истечения 999 мс.
EDIT: приведенный ниже код может задержать выполнение без возможности two
напечатать перед one
function(){
setTimeout(function() {
$("#output").append(" one ");
setTimeout(function() {
$("#output").append(" two ");
}, 100);
}, 1000);
}
Ответ 3
Да, поскольку код javascript выполняется в одном потоке, все асинхронные события, такие как click
, mousemove
, помещаются в очередь для выполнения. Когда вы вызываете setTimeout
, двигатель вставляет таймер в свою очередь для выполнения в будущем, по крайней мере, после delay
времени. Таким образом, два setTimeout
генерируют два таймера один за другим.
Вы можете посмотреть Как работает Javascript Timers от Джона Ресига.
Ответ 4
Нет, вы не можете быть уверены. Это асинхронно.
Но на самом деле это, вероятно, будет верно, из-за реализации механизма в браузерах.
Ответ 5
Да, выход всегда будет "one two"
.
Поскольку первый setTimeout
всегда работает до второго setTimeout
, если у них одинаковое время задержки, тогда функция обратного вызова first всегда будет выполняться до функции обратного вызова второго.
Ответ 6
Обновление
Я обновил вашу скрипку с помощью этого метода. Проверьте здесь
Как насчет этого метода - Получите список вещей, которые нужно сделать с вашего сервера -
//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);
});
Таким образом, вы определяете последовательность.
Ответ 7
Если события синхронны, существует функция 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
, а порядок выполнения все еще function1
, затем function2
.
Другой способ вызова:
window.onload = function() {
var key = [];
document.getElementById("test1").addEventListener('click', keyedSequence(key, function1), false);
document.getElementById("test2").addEventListener('click', keyedSequence(key, function2), false);
}