web-dev-qa-db-ja.com

forEachループで各反復を実行した後に遅延を追加する

ForEach(プレーンのJavaScript)で反復を遅くする簡単な方法はありますか?例えば:

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

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

あなたが達成したいことは_Array#forEach_で完全に可能です—別の方法でそれを考えるかもしれません。次のようなことはできますできません

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

...そして出力を取得します:

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

JavaScriptには、その後のすべてのコードをブロックする同期wait関数またはsleep関数はありません。

JavaScriptで何かを遅延させる唯一の方法は、非ブロッキング方法です。つまり、 setTimeout またはその親族の1つを使用します。 _Array#forEach_に渡す関数の2番目のパラメーターを使用できます。これには、現在の要素のインデックスが含まれます。

_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.');_

indexを使用して、関数をいつ実行するかを計算できます。しかし、今は別の問題があります。console.log('Loop finished.')は、ループの最初の反復の前にbefore実行されます。これは、setTimoutが非ブロッキングであるためです。

JavaScriptはループ内でタイムアウトを設定しますが、タイムアウトが完了するまで待機しません。 forEachの後にコードを実行し続けるだけです。

これを処理するには、Promisesを使用できます。プロミスチェーンを構築しましょう:

_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.');
});_

Promise/forEach/mapと組み合わせたfiltersに関する優れた記事があります here


配列が動的に変化する可能性がある場合は、さらに注意が必要です。その場合、_Array#forEach_を使用する必要はないと思います。代わりにこれを試してください:

_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>_
26
PeterMader

遅延を作成し、再帰的に実装するには、setTimeoutを使用する必要があります

あなたの例は次のようになります

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)
3
Shubham Khatri

再帰が最も簡単な解決策を提供すると思います。

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

ジェネレータ

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

これにより、リスト内の次のイテレーションにいつアクセスするかを完全に制御できます。

0
monners

まず、コードを変更する必要があります。

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

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

ForEachを使用すると、JavaScriptで配列を簡単にループできますが、残念ながら、querySelectorAllの結果はそれほど単純ではありません。

Itについてもっと読む ここ

私はあなたにこれを読むようアドバイスすることができます answer 睡眠のための正しい解決策を見つけるために

0
Ali Mamedov

_async/await_、Promiseコンストラクター、setTimeout()および_for..of_ループを使用して、durationを前に設定できるタスクを順番に実行できます。タスクが実行されます

_(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