Вложенная альтернатива setTimeout?
Мне нужно выполнить 3 функции за 1 секунду.
для простоты эти функции:
console.log('1');
console.log('2');
console.log('3');
Я мог бы сделать это: (очень уродливо)
console.log('1')
setTimeout(function () {
setTimeout(function () {
console.log('2')
setTimeout(function () {
console.log('3')
}, 1000)
}, 1000)
}, 1000)
Или я мог бы создать array
функций и использовать setInterval
с помощью счетчика global
.
Есть ли элегантный способ сделать это?
(p.s. функция no.2 не зависит от номера функции 1... следовательно, каждая секунда выполняет следующую функцию.).
Ответы
Ответ 1
Вы можете использовать что-то вроде этого с помощью setTimeout
:
var funcs = [func1, func2, func3],
i = 0;
function callFuncs() {
funcs[i++]();
if (i < funcs.length) setTimeout(callFuncs, 1000);
}
setTimeout(callFuncs, 1000); //delay start 1 sec.
или начните с простого вызова callFuncs() напрямую.
Обновление
Подход setInterval
(обратите внимание на риск укладки вызовов):
var funcs = [func1, func2, func3],
i = 0,
timer = setInterval(callFuncs, 1000);
function callFuncs() {
funcs[i++]();
if (i === funcs.length) clearInterval(timer);
}
Ответ 2
Предполагая, что вы запускаете его в современном браузере или добавили поддержку array.map, это довольно кратким:
[func1, func2, func3].map(function (fun, index) {
setTimeout(fun, 1000 + index * 1000);
}
Ответ 3
setTimeout(function(){console.log('1')}, 1000);
setTimeout(function(){console.log('2')}, 2000);
setTimeout(function(){console.log('3')}, 3000);
Ответ 4
Я думаю, что самый простой способ сделать это - создать некоторые замыкания внутри функции.
Во-первых, я вспомню, что у вас большой интерес к использованию setInterval
, поскольку накладные расходы setTimeout
могут привести к срабатыванию 10 мс. Поэтому, особенно если использовать короткий (< 50ms) интервал, предпочитайте setInterval.
Поэтому нам нужно сохранить массив функций, индекс последней выполняемой функции и ссылку на интервал, чтобы остановить вызовы.
function chainLaunch(funcArray, time_ms) {
if (!funcArray || !funcArray.length) return;
var fi = 0; // function index
var callFunction = function () {
funcArray[fi++]();
if (fi==funcArray.length)
clearInterval(chainInterval);
} ;
var chainInterval = setInterval( callFunction, time_ms);
}
Rq: Вы можете скопировать массив функций (funcArray = funcArray.slice(0);
)
Rq2: вам может понадобиться цикл внутри массива
Rq3: вы можете принять дополнительные аргументы для chainlaunch. извлеките их с помощью var funcArgs = arguments.slice(3);
и примените к функциям: funcArray[fi++].apply(this,funcArgs);
В любом случае работает следующий тест:
var f1 = function() { console.log('1'); };
var f2 = function() { console.log('2'); };
var f3 = function() { console.log('3'); };
var fArr = [f1, f2, f3];
chainLaunch(fArr, 1000);
как вы можете видеть в этой скрипте: http://jsfiddle.net/F9UJv/1/
(откройте консоль)
Ответ 5
Существует новый тип объявления функции, называемый генераторами в es6 (a.k.a ecmascript 6, es2015). Это невероятно полезно для этой ситуации, и ваш асинхронный код выглядит синхронным. es6 является последним стандартом JavaScript по состоянию на 2015 год. Он работает на современных браузерах, но вы можете использовать Babel и его javascript polyfill для использования генераторы теперь даже в старых браузерах.
Здесь - это учебник по генераторам.
Функция myDelayedMessages ниже - пример генератора. Run - это вспомогательная функция, которая выполняет функцию генератора, которую он вызывает, и предоставляет функцию для продвижения генератора в качестве первого аргумента функции генератора, которую он вызывал.
function delay(time, callback) {
setTimeout(function () {
callback();
}, time);
}
function run(generatorFunction) {
var generatorItr = generatorFunction(resume);
function resume(callbackValue) {
generatorItr.next(callbackValue);
}
generatorItr.next()
}
run(function* myDelayedMessages(resume) {
for(var i = 1; i <= 3; ++i) {
yield delay(1000, resume);
console.log(i);
}
});
Ответ 6
Здесь есть два метода. Один с setTimeout и другой с setInterval. Первое, на мой взгляд, лучше.
for(var i = 1; i++; i<=3) {
setTimeout(function() {
console.log(i);
}, 1000*i);
}
// second choice:
var i = 0;
var nt = setInterval(function() {
if(i == 0) return i++;
console.log(i++);
if(i>=3) clearInterval(nt);
}, 1000);