使用forEach循环执行每次迭代后添加延迟

时间:2017-08-04 05:30:19

标签: javascript loops foreach delay pause

有没有一种简单的方法可以减慢forEach中的迭代速度(使用普通的javascript)?例如:

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

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

8 个答案:

答案 0 :(得分:18)

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中没有同步waitsleep函数阻止其后的所有代码。

在JavaScript中延迟某些内容的唯一方法是以非阻塞方式。这意味着使用setTimeout或其中一个亲戚。我们可以使用传递给Array#forEach的函数的第二个参数:它包含当前元素的索引:



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.')在循环的第一次迭代之前执行。这是因为setTimout是非阻止的。

JavaScript在循环中设置超时,但它不会等待超时完成。它只是继续执行forEach之后的代码。

为了解决这个问题,我们可以使用Promise s。让我们建立一个承诺链:



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 herefilter一篇优秀文章。

如果数组可以动态更改,我会变得更加棘手。在这种情况下,我不认为应该使用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 = '';
});
&#13;
<input type="text">
<button>Add to array</button>
&#13;
&#13;
&#13;

答案 1 :(得分:2)

您需要使用setTimeout来创建延迟并具有递归实现

您的示例应该看起来像

&#13;
&#13;
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)
&#13;
&#13;
&#13;

答案 2 :(得分:0)

首先,您必须更改代码:

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

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

您可以使用forEach在JavaScript中轻松遍历数组,但是   不幸的是,它的结果并不那么简单   querySelectorAll

详细了解here

我可以建议您阅读此answer以找到适合睡眠的解决方案

答案 3 :(得分:0)

您可以使用async/awaitPromise构造函数,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});
  };
})();

答案 4 :(得分:0)

我认为递归提供了最简单的解决方案。

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

答案 5 :(得分:0)

Generators

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

这使您可以完全控制何时访问列表中的下一个迭代。

答案 6 :(得分:0)

您可以做出承诺并将其与for一起使用,该示例必须位于async / await函数中:

    let myPromise = () => new Promise((resolve, reject) => {
      setTimeout(function(){
        resolve('Count')
      }, 1000)
    })
  
    for (let index = 0; index < 100; index++) {
      let count = await myPromise()
      console.log(`${count}: ${index}`)    
    }

答案 7 :(得分:0)

使用 JS Promises 和 paid 语法,您可以制作一个真正有效的 asnyc/await 函数。但是,sleep 会同步调用每次迭代,因此您会得到 1 秒的延迟,然后会同时调用所有项目。

forEach

我们可以做的是使用 const items = ["abc", "def", "ghi", "jkl"]; const sleep = (ms) => new Promise((res) => setTimeout(res, ms)); items.forEach(async (item) => { await sleep(1000); console.log(item); });setInterval(或 clearInterval,但我们使用前者)来进行定时 forEach 循环,如下所示:

setTimeout