web-dev-qa-db-fra.com

Ajouter un délai après chaque itération avec la boucle forEach

Existe-t-il un moyen simple de ralentir l’itération dans un forEach (avec javascript simple)? Par exemple:

var items = document.querySelector('.item');

items.forEach(function(el) {
  // do stuff with el and pause before the next el;
});
6
Kirk Ross

Ce que vous voulez réaliser est tout à fait possible avec Array#forEach - bien que vous puissiez le penser différemment. Vous pouvez pas faire une chose comme ceci:

var array = ['some', 'array', 'containing', 'words'];
array.forEach(function (el) {
  console.log(el);
  wait(1000); // wait 1000 milliseconds
});
console.log('Loop finished.');

... et obtenez le résultat:

some
array          // one second later
containing     // two seconds later
words          // three seconds later
Loop finished. // four seconds later

Il n'y a pas de fonction wait ou sleep synchrone dans JavaScript qui bloque tout le code qui la suit.

La seule façon de retarder quelque chose en JavaScript est de ne pas bloquer. Cela signifie qu’il faut utiliser setTimeout ou l’un de ses parents. Nous pouvons utiliser le second paramètre de la fonction que nous passons à Array#forEach: il contient l'index de l'élément en cours:

var array = ['some', 'array', 'containing', 'words'];
var interval = 1000; // how much time should the delay between two iterations be (in milliseconds)?
array.forEach(function (el, index) {
  setTimeout(function () {
    console.log(el);
  }, index * interval);
});
console.log('Loop finished.');

En utilisant index, nous pouvons calculer quand la fonction doit être exécutée. Mais nous avons maintenant un problème différent: la console.log('Loop finished.') est exécutée avant la première itération de la boucle. En effet, setTimout est non bloquant.

JavaScript définit les délais d'attente dans la boucle, mais il n'attend pas la fin des délais. Il continue simplement à exécuter le code après la forEach.

Pour gérer cela, nous pouvons utiliser Promises. Construisons une chaîne de promesses:

var array = ['some', 'array', 'containing', 'words'];
var interval = 1000; // how much time should the delay between two iterations be (in milliseconds)?
var promise = Promise.resolve();
array.forEach(function (el) {
  promise = promise.then(function () {
    console.log(el);
    return new Promise(function (resolve) {
      setTimeout(resolve, interval);
    });
  });
});

promise.then(function () {
  console.log('Loop finished.');
});

Il existe un excellent article sur Promises en conjonction avec forEach/map/filterici .


Je deviens plus difficile si le tableau peut changer dynamiquement. Dans ce cas, je ne pense pas que Array#forEach devrait être utilisé. Essayez ceci à la place:

var array = ['some', 'array', 'containing', 'words'];
var interval = 2000; // how much time should the delay between two iterations be (in milliseconds)?

var loop = function () {
  return new Promise(function (outerResolve) {
    var promise = Promise.resolve();
    var i = 0;
    var next = function () {
      var el = array[i];
      // your code here
      console.log(el);
      if (++i < array.length) {
        promise = promise.then(function () {
          return new Promise(function (resolve) {
            setTimeout(function () {
              resolve();
              next();
            }, interval);
          });
        });
      } else {
        setTimeout(outerResolve, interval);
        // or just call outerResolve() if you don't want to wait after the last element
      }
    };
    next();
  });
};

loop().then(function () {
  console.log('Loop finished.');
});

var input = document.querySelector('input');
document.querySelector('button').addEventListener('click', function () {
  // add the new item to the array
  array.Push(input.value);
  input.value = '';
});
<input type="text">
<button>Add to array</button>

16
PeterMader

Vous devez utiliser setTimeout pour créer un délai et avoir une implémentation récursive.

Votre exemple devrait ressembler à

var items = ['a', 'b', 'c']
var i = 0;
(function loopIt(i) {
  setTimeout(function(){
      // your code handling here
      console.log(items[i]);
      if(i < items.length - 1)  loopIt(i+1)
    }, 2000);
})(i)

2
Shubham Khatri

Je pense que la récursivité offre la solution la plus simple.

function slowIterate(arr) {
  if (arr.length === 0) {
    return;
  }
  console.log(arr[0]); // <-- replace with your custom code 
  setTimeout(() => {
    slowIterate(arr.slice(1));
  }, 1000); // <-- replace with your desired delay (in milliseconds) 
}

slowIterate(Array.from(document.querySelector('.item')));
0
David L. Walsh

Générateurs

function* elGenLoop (els) {
  let count = 0;

  while (count < els.length) {
    yield els[count++];
  }
}

// This will also work with a NodeList
// Such as `const elList = elGenLoop(document.querySelector('.item'));`
const elList = elGenLoop(['one', 'two', 'three']);

console.log(elList.next().value); // one
console.log(elList.next().value); // two
console.log(elList.next().value); // three

Cela vous donne un contrôle complet sur le moment auquel vous souhaitez accéder à la prochaine itération de la liste.

0
monners

Tout d'abord, vous devez changer votre code:

var items = document.querySelectorAll('.item'), i;

for (i = 0; i < items.length; ++i) {
  // items[i] <--- your element
}

Vous pouvez parcourir facilement les tableaux en JavaScript avec forEach, mais Malheureusement, ce n'est pas si simple avec les résultats d'un querySelectorAll

En savoir plus sur It here

Je peux vous conseiller de lire ceci répondre pour trouver la bonne solution pour dormir

0
Ali Mamedov

Vous pouvez utiliser les constructeurs async/await, Promise, setTimeout() et for..of pour exécuter des tâches dans l'ordre, une duration pouvant être définie avant l'exécution d'une tâche

(async() => {

  const items = [{
    prop: "a",
    delay: Math.floor(Math.random() * 1001)
  }, {
    prop: "b",
    delay: 2500
  }, {
    prop: "c",
    delay: 1200
  }];

  const fx = ({prop, delay}) =>
    new Promise(resolve => setTimeout(resolve, delay, prop)) // delay
    .then(data => console.log(data)) // do stuff

  for (let {prop, delay} of items) {
    // do stuff with el and pause before the next el;
    let curr = await fx({prop, delay});
  };
})();

0
guest271314