question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. ItĀ collects links to all the places you might be looking at while hunting down a tough bug.

And, if youā€™re still stuck at the end, weā€™re happy to hop on a call to see how we can help out.

Awaiting inside a setInterval prevents setTimeout from being called

See original GitHub issue

šŸ› Bug Report

I am trying to mock a setTimeout inside of a setInterval with Jest, but am seeing confusing behavior with async/await and I do not know whether it is a bug, or if I am doing something wrong.

Awaiting inside the async function passed to setInterval causes setTimeout to never run. If I remove the await in the function below, the setTimeout is successfully set.

To Reproduce

See updated Reproduction Case in my Below Comment

// With await, setTimeout is never called

let interval;

async function check() {
  await Promise.resolve(true); //does not execute the setTimeout below
  setTimeout(() => {
    console.log('Hello World');
    clearInterval(interval);
  }, 1000);
}

const testFunction = async () => {
  interval = setInterval(check, 1000);
};

describe('Timers', () => {
  beforeEach(() => {
    jest.useFakeTimers();
  });

  it('should set a timeout that prints Hello World', async () => {
    await testFunction();

    jest.advanceTimersByTime(100000);
  });
});
// Removing the await, the setTimeout is executed

let interval;

async function check() {
  Promise.resolve(true);
  setTimeout(() => {
    console.log('Hello World');
    clearInterval(interval);
  }, 1000);
}

const testFunction = async () => {
  interval = setInterval(check, 1000);
};

describe('Timers', () => {
  beforeEach(() => {
    jest.useFakeTimers();
  });

  it('should set a timeout that prints Hello World', async () => {
    await testFunction();

    jest.advanceTimersByTime(100000);
  });
});

Expected behavior

setTimeout to run after a promise resolves.

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:2
  • Comments:6

github_iconTop GitHub Comments

2reactions
DaveSteincommented, Feb 26, 2020

@SimenB ran into this. Removed jest.useFakeTimers, issue was resolved. My workaround was:

beforeEach(() => {
  jest.spyOn(global, 'setTimeout');
});

afterEach(() => {
  global.setTimeout.mockRestore();
});

it('test code', async () => {
  global.setTimeout.mockImplementation(callback => callback());
  await theMethodThatHasSetTimeoutWithAwaitInsideCallback();
  expect(global.setTimeout).toBeCalledWith(expect.any(Function), 25000);
});
1reaction
OogieBoogieInJSONcommented, Dec 19, 2019

I have this issue as well.

Given a function that creates a setInterval inside. setIntervalā€™s callback is an async function, which awaits for something and then invokes another function (Iā€™m testing the result of this invoked function).

In my tests Iā€™m using runOnlyPendingTimers or advanceTimersByTime but by the time I do the expect await inside the function I test is triggered afterwards, so my assert fails.

Iā€™ve tried adding await Promise.resolve() after advancing my timers but still nothing changes.

So, I donā€™t think this is a bug, the timers are advanced but thereā€™s no way to wait for that await šŸ˜¦

Later edit:

If i stack

await Promise.resolve();
await Promise.resolve();
await Promise.resolve();
expect(...)

This seems to make it work but I donā€™t really want to do that.

Later later edit:

I made it work somehow, hereā€™s an example:

const testPromise= Promise.resolve({ yourData });
const mockFnInsideInterval = jest
      .spyOn(module, 'test')
      .mockImplementation(() => testPromise);
invokeWrappingFunction();
jest.advanceTimersByTime(...);
await testPromise;
expect(...);

This seems to make it work

Read more comments on GitHub >

github_iconTop Results From Across the Web

Jest: awaiting inside a setInterval prevents setTimeout from ...
I am trying to mock a setTimeout inside of a setInterval with Jest, but am seeing confusing behavior and I do not know...
Read more >
Scheduling: setTimeout and setInterval
When a function is passed in setInterval/setTimeout , an internal reference is created to it and saved in the scheduler. It prevents theĀ ......
Read more >
Asynchronous JavaScript 1 of 3: setTimeout, setInterval ...
This is the first episode in a set of 3 where we'll talk about how JavaScript runs asynchronously. In this episode, we'll use...
Read more >
setTimeout vs setInterval | Top 4 Differences You Should Know
The setTimeout is function used as settimeout(). It is used for executing a defined block of code after a specific time period, and...
Read more >
Using setTimeout() and other timer APIs in Node.js
setTimeout () returns a Timeout object, which may be used to terminate the timeout using the clear method known as clearTimeout() . One...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found