Home > Blockchain >  Testing using setTimeout(fn, 0). What this snippet of testing code does?
Testing using setTimeout(fn, 0). What this snippet of testing code does?

Time:05-17

This is a snippet from a unit test file that use jest. The parse function returns a Promise after the parse is completed... But I cannot understand the use of that boolean flag (completedAsyncDummyTask).

it("parse is a promise that resolves with parser output", async () => {
  const parser = new Parser();
  let completedAsyncDummyTask = false;

  setTimeout(() => {
    completedAsyncDummyTask = true;
  }, 0);
  
  ...
  const test = await parser.parse(path.resolve(__dirname, "file.xyz"));
  expect(completedAsyncDummyTask).toBe(true);
  ...

});

Any ideas?

Thanks in advance.

CodePudding user response:

I can't imagine what the purpose of the test would be. I can tell you what it's doing, but not why.

It's checking that the process of parser.parse(path.resolve(__dirname, "file.xyz")) fulfilled its promise while allowing at least one loop through the main ("macro") task queue, not just by doing all its work within the micro-task queue used by promise fulfillment.

If a promise is fulfilled right away, the main task queue never has a chance to run before code using await on it runs:

(async () => {
    setTimeout(() => {
        console.log("This comes second because it queues a macrotask");
    }, 0);
    await Promise.resolve();
    console.log("This comes first because the promise fulfillment never allowed the macro task queue to be processed.");
})();

But if it isn't, the main task queue might be processed at least once, which gives that timer callback a chance to set the variable.

The presence of the test suggests there's some code in the app that relies on that behavior, which would be quite odd. Even more odd that there's no comment explaining such an obscure test.


You might be tempted to think that since Promise.resolve returns a fulfilled promise, the await doesn't do anything at all and that code is just run synchronously. That's not true, even awaiting a fulfilled promise involves delaying subsequent code at least until the microtask queue has cycled, as we can see here:

let run = true;
let ticks = 0;
(async () => {
    while (run) {
        await Promise.resolve();
          ticks;
    }
})();
(async () => {
    setTimeout(() => {
        console.log(`This comes second because it queues a macrotask`);
    }, 0);
    console.log(`Before: ticks = ${ticks}`);
    await Promise.resolve();
    console.log(`After:  ticks = ${ticks}`);
    console.log("This comes first because the promise fulfillment never allowed the macro task queue to be processed.");
    run = false;
})();

  • Related