setTimeout и массив каждый

Я смущен использованием setTimeout и каждого итератора. Как я могу переписать следующее, чтобы консоль выводила каждое имя после задержки в 5 секунд? В настоящее время код ниже выводит все имена сразу после 5 секунд. Я хотел бы:

1) подождите 5 секунд, затем распечатайте kevin
2) подождите 5 секунд, затем распечатайте mike
3) подождите 5 секунд, затем распечатайте sally

var ary = ['kevin', 'mike', 'sally'];

_(ary).each(function(person){

  setTimeout(function(){
    console.log(person);
  }, 5000);    

});

5 ответов


вы можете создать переменную с именем offset это заставляет таймер ждать еще 5 секунд для каждого человека в массиве, например:

var ary = ['kevin', 'mike', 'sally'];

var offset = 0;
_(ary).each(function(person){

  setTimeout(function(){
    console.log(person);
  }, 5000 + offset);    
 offset += 5000;
});

вы могли бы сделать

var ary = ['kevin', 'mike', 'sally'];

_(ary).each(function(person, index){

  setTimeout(function(){
    console.log(person);
  }, index * 5000);    
});

без увеличения timeout значение, вы инициализируете все setTimeouts С точно таким же значением (thats, почему вы видите то, что вы видите).


у вас есть три основных варианта:

  1. Для Петли + setTimeout
    ... инициализируйте всех немедленно, но пошатните время начала на основе позиции индекса, чтобы они не все шли одновременно.
  2. setTimeout + условный рекурсии
    ... зайдите в каждый n секунд - и я скажу вам, если вам нужно сделать другой
  3. setInterval + условное clearInterval
    ... продолжайте работать каждый n секунды-пока я не скажу тебе остановиться

вот каждый из тех, кто облекся с рабочим примером:

1. Для Петли + setTimeout

пара заметок об этом. Это похоже на запуск эстафеты и раздачу инструкций заранее каждому бегуну, чтобы начать точно в 5: 00 и 5:02 и 5: 04, независимо от того, закончил ли человек за ними долгое время давно или еще не приехал.

кроме того, вы не можете использовать обычный for i=0 loop, поскольку оператор for не определяет область новой функции. Таким образом, значения объектов, установленные в каждом цикле for, будут применяться на итерациях. К моменту вызова setTimeout будет использоваться только самое последнее значение. Поэтому нам нужно закрытие, чтобы сохранить значение в каждом цикле. Я использовал Array.prototype.forEach(), но если вы хотите использовать реализации forEach в jQuery или подчеркивании, это будет работать тоже.

function ArrayPlusDelay(array, delegate, delay) {
 
  // initialize all calls right away
  array.forEach(function (el, i) {
    setTimeout(function() {
        // each loop, call passed in function
        delegate( array[i]);

      // stagger the timeout for each loop by the index
      }, i * delay);
  })
 
}

// call like this
ArrayPlusDelay(['a','b','c'], function(obj) {console.log(obj)},1000)

2. setTimeout + условный рекурсии

для двух нижних вариантов мы делаем свои собственные циклы, поэтому нам придется самим отслеживать индекс, инициализируя его на нуле и увеличивая на всем протяжении.

для этого, мы будем a) вызов setTimeout который будет выполняться один раз, b) оцените массив в позиции индекса, c) проверьте, есть ли больше элементов в массиве, и если да, начните с (ля.)

   
function ArrayPlusDelay(array, delegate, delay) {
  var i = 0
  
  function loop() {
  	  // each loop, call passed in function
      delegate(array[i]);
      
      // increment, and if we're still here, call again
      if (i++ < array.length - 1)
          setTimeout(loop, delay); //recursive
  }

  // seed first call
  setTimeout(loop, delay);
}

// call like this
ArrayPlusDelay(['d','e','f'], function(obj) {console.log(obj)},1000)

3. setInterval + условный clearInterval

Примечание функция setInterval будет работать вечно, когда-то называли. Это возвращаемое значение, когда изначально установлено, будет содержать ссылку на интервал, поэтому оно часто сочетается с функцией clearInterval по желанию отключить его по дороге

function ArrayPlusDelay(array, delegate, delay) {
  var i = 0
  
   // seed first call and store interval (to clear later)
  var interval = setInterval(function() {
    	// each loop, call passed in function
      delegate(array[i]);
      
        // increment, and if we're past array, clear interval
      if (i++ >= array.length - 1)
          clearInterval(interval);
  }, delay)
  
}

ArrayPlusDelay(['x','y','z'], function(obj) {console.log(obj)},1000)

3* Секретный Четвертый Вариант (Лучший)

опции 1 и 2 рискованны, потому что, как только вы отправите этот поезд, нет возможности отменить его по дороге (сохранить закрытие браузера). Если у вас большой массив или большая нагрузка в вашем делегате, было бы неплохо предоставить некоторый ресурс, если вам это нужно. Сохранив ссылку из setInterval, у нас будет постоянный доступ к итерационной функции. Нам просто нужно вернуть объект interval выше и сохранить его при вызове нашего массива plus delay функция.

function ArrayPlusDelay(array, delegate, delay) {
  var i = 0
  
   // seed first call and store interval (to clear later)
  var interval = setInterval(function() {
    	// each loop, call passed in function
      delegate(array[i]);
      
        // increment, and if we're past array, clear interval
      if (i++ >= array.length - 1)
          clearInterval(interval);
  }, delay)
  
  return interval
}

var inter = ArrayPlusDelay(['x','y','z'], function(obj) {console.log(obj)},1000)

тогда, если мы когда-нибудь захотим очистить его позже, просто бросьте это в консоль:

clearInterval(inter);

все 3 демонстрации в jsFiddle

Подобные Вопросы Переполнения Стека:


each обычно лучше для вещей, которые происходят немедленно.

вместо этого, если вы не против изменить массив, вы можете использовать его в качестве очереди:

var ary = ['kevin', 'mike', 'sally'];

setTimeout(function loop() {
    console.log(ary.shift());

    if (ary.length)
        setTimeout(loop, 5000);
}, 5000);

он продолжает звонить loop 5 секунд в будущем, пока в очереди ничего не останется.


вы можете просто использовать setInterval () С простым счетчиком увеличения на один.

var ary = ['kevin', 'mike', 'sally'];

var i=0;
setInterval(function(){
    console.log(ary[i]);
    i++;
}, 5000);

но обратите внимание, что это начнет бросать ошибки после того, как i станет больше двух. У вас должна быть какая-то проверка и убедитесь, что вы очистили интервал.