Search code examples
javascriptasynchronoussettimeoutes6-promise

Print numbers in order, delay with which the element appears is dependent on the sequence number of the element


I want to print a sequence of numbers; it should always start with 1 and be shown after a delay of 1s (1000ms). The next number in the list, 2 should be printed after a delay of 2 seconds, after the previous one, i.e 1 has been printed and so on until the given limit is reached. It has be done sequentially.

1 should print at time 1 second

2 should print at time 3 seconds

3 should print at time 6 seconds and so on..

I had two approaches in mind, either using Promises or using Generator function/async-await

My question is regarding the first approach, i.e, using Promise.

a. When using setTimeout, I am not getting the correct order

b. Whereas using a block/delay function gives me the correct result.

a. When using setTimeout, I am not getting the correct order

Reasons which contradict this behavior: Every task passed to the Timer functionality of the browser/Node.js, waits for the timeout to be completed, once done, pushes the task to the call stack.

Promise.then guarantees that the previous thenable is resolved before taking up the next function/callback form the onFullfillment array.

Can someone please advise me with a correct intuition or help me with correct understanding.

(function printAsyncInOrder(limit) {
  let [, ...list] = [...Array(limit + 1).keys()];

  list.reduce(chainedSequence, Promise.resolve());

  function chainedSequence(chain, currentOrder, index) {
    return chain.then(() => {
      let i = index;
      let current = currentOrder;
      //  setTimeout(() => console.log(`Hi ${index} ${new Date()}`), i * 1000)

      blocker(current * 1000);
    });
  }

  function blocker(time) {
    let timeNow = new Date();
    while (new Date() - timeNow < time) {}
    console.log(`hi ${time} ${new Date()}`);
  }
})(10);

Using setTimeout:

Hi 2 Sun Nov 10 2019 11:37:44
Hi 3 Sun Nov 10 2019 11:37:45
Hi 4 Sun Nov 10 2019 11:37:46
Hi 5 Sun Nov 10 2019 11:37:47
Hi 6 Sun Nov 10 2019 11:37:48
Hi 7 Sun Nov 10 2019 11:37:49
Hi 8 Sun Nov 10 2019 11:37:50
Hi 9 Sun Nov 10 2019 11:37:51
Hi 10 Sun Nov 10 2019 11:37:52

using blocker/delay function

hi 2000 Sun Nov 10 2019 11:38:29
hi 3000 Sun Nov 10 2019 11:38:32
hi 4000 Sun Nov 10 2019 11:38:36
hi 5000 Sun Nov 10 2019 11:38:41
hi 6000 Sun Nov 10 2019 11:38:47
hi 7000 Sun Nov 10 2019 11:38:54
hi 8000 Sun Nov 10 2019 11:39:02
hi 9000 Sun Nov 10 2019 11:39:11
hi 10000 Sun Nov 10 2019 11:39:21

Solution

  • you'll need to return a Promise in chain.then(() => { that resolves after the timeout expires

    like this

    (function printAsyncInOrder(limit) {
      let [, ...list] = [...Array(limit + 1).keys()];
    
      list.reduce(chainedSequence, Promise.resolve());
    
      function chainedSequence(chain, currentOrder, index) {
        return chain.then(() => {
          let current = currentOrder;
          return new Promise(resolve => setTimeout(() => {
            console.log(`Hi ${index} ${new Date()}`);
            resolve();
          }, 1000 * index));
        });
      }
    })(10);