Understanding Sinon clock `tick` vs `tickAasync`

Viewed 225

I am trying to understand the difference between clock.tick and clock.tickAsync and where should I use each one. The explanation in the docs is not clear to me, and by looking at the code itself I still can't understand where and when should I use each method. Can anyone help me with a concrete code example?

1 Answers

The tickAsync() will also break the event loop, allowing any scheduled promise callbacks to execute before running the timers.

First, I recommend looking the video about event loop.

If short, the difference between tick and tickAsync is that tick - is call timers synchronous and tickAsync is - call timers asynchronous that allow to execute resolved promises before calling timers

What synchronous means. Let's look at the example code which we would test

function myFunction() {
  setTimeout(() => console.log('foo'), 1000)
  Promise.resolve().then(() => console.log('zoo'));
  console.log('bar')
}

and our synchronous test code

it('should test synchronously', () => {
   myFunction()
   tick(1000); // This would call scheduled timers synchronously ⚠️
   console.log('baz')
});
// print 'bar'
// print 'foo'
// print 'baz'
// print 'zoo'

and now let's see the same test code but with tickAsync

it('should test synchronously', () => {
   myFunction()
   tickAsync(1000);
   console.log('baz')
});
// print 'bar'
// print 'baz' 
// print 'zoo' 
// print 'foo' 

As you can see, the order of printed string is changed. Now the same code but with await

it('should test synchronously', async () => {
   myFunction()
   await tickAsync(1000);
   console.log('baz')
});
// print 'bar'
// print 'zoo' 
// print 'foo' 
// print 'baz' 

To understand why the order changed, you should understand the difference betweens micro tasks queue and regular queue. But if you watched the video from link above, you already should get the idea

P.S. Also, you should understand that sinon.useFakeTimers(); will replace setTimeout and setInterval on own implementation that allow sinon to have full control over your timers passed to these functions

This is a very rough approximation how sinon tick & tickAsync implemented

scheduledTimers = [];

function useFakeTimers() {
    global.setInterval = global.setTimeout = (fn) => {
        scheduledTimers.push(fn);
    }
}

function tick() {
    while (scheduledTimers.length) {
        scheduledTimers.shift()();
    }
}

function tickAsync() {
    // Call timers on the next tick
    return Promise.resolve().then(() => tick());
}
Related