Search code examples
javascriptecmascript-6es6-promise

ES6 Iterator and settimeout


I am trying to iterate a list of requests, but have a delay before the next request is processed. I thought of doing this using ES6 Iterator as follows but it does not work... (the console output freezes for me, not sure why)

requests = [1,2,3,4,5,6];
delay    = 2000;

const iterateCollection = requests => {
return new Promise((resolve, reject) => {

    function *iterateRequests() {
        for (const req of requests) {
            yield req;
        }
    }

    const requestIterator = iterateRequests();
    let   currentRequest  = requestIterator.next();

    while(!currentRequest.done) {
        setTimeout(() => { 
            console.log(currentRequest);
            currentRequest = requestIterator.next();
        }, delay);
    }

    resolve();
}); 
};

iterateCollection(requests)
    .then(() => console.log('done!'));

It seems that the setTimeOut is not being liked. If I remove it from the code (remove the delay), I get the expected iteration without the delay like so..

requests = [1,2,3,4,5,6];
delay    = 2000;

const iterateCollection = requests => {
  return new Promise((resolve, reject) => {

    function *iterateRequests() {
        for (const req of requests) {
            yield req;
        }
    }

    const requestIterator = iterateRequests();
    let   currentRequest  = requestIterator.next();

    while(!currentRequest.done) {
        console.log(currentRequest);
        currentRequest = requestIterator.next();
    }

    resolve();
  });   
};

iterateCollection(requests)
    .then(() => console.log('done!'));

// OUTPUT
//{value: 1, done: false}
//VM85:26 {value: 2, done: false}
//VM85:26 {value: 3, done: false}
//VM85:26 {value: 4, done: false}
//VM85:26 {value: 5, done: false}
//VM85:26 {value: 6, done: false}
//VM85:35 done!

Solution

  • Async things are enqueued in the so called event queue, until the js engine is done with its current task, then it takes the next task from the event queue. In your case, the current task is this:

    while(!currentRequest.done) {
    

    As your array has an element, the iterator is not done and the loop will run forever. All the timeouts get stuck in the event queue, the handlers will be never executed. You may just use an async so you can await the loop until the timeout is done:

     const timer = ms => new Promise(res => setTimeout(res, ms));
    
    async function iterateCollection(collection){
      for(const el of collection){
        await timer(1000);
         //...
      }
    }