Вложенная альтернатива 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);