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.

[Bug]: Async function with multiple awaits never resolves when using fake timers

See original GitHub issue

Version

27.2.0

Steps to reproduce

  1. Clone my repo: https://github.com/srmagura/jest-timer-repro
  2. yarn install
  3. yarn test

Code:

import "jest";

beforeEach(() => {
  jest.useFakeTimers();
});

function delay(duration: number): Promise<void> {
  return new Promise((resolve) => setTimeout(resolve, duration));
}

// PASS
it("delays", async () => {
  const p = delay(100);
  jest.runAllTimers();

  expect(await p).toBeUndefined();
});

// PASS
it("delays once in a function", async () => {
  async function f(): Promise<void> {
    await delay(100);
  }

  const p = f();
  jest.runAllTimers();

  expect(await p).toBeUndefined();
});

// FAIL: Exceeded timeout of 5000 ms for a test.
it("delays twice in a function", async () => {
  async function f(): Promise<void> {
    await delay(100);
    await delay(100);
  }

  const p = f();
  jest.runAllTimers();
  jest.runAllTimers();
  jest.runAllTimers();
  jest.runAllTimers();

  expect(await p).toBeUndefined();
});

// PASS
it("delays twice in a function using real timers", async () => {
  jest.useRealTimers();

  async function f(): Promise<void> {
    await delay(100);
    await delay(100);
  }

  const p = f();
  expect(await p).toBeUndefined();
});

Expected behavior

I expect all tests to pass. In the third test, jest.runAllTimers should cause both delays to complete, and the promise returned by f should resolve.

Actual behavior

The third test fails with

thrown: "Exceeded timeout of 5000 ms for a test.
    Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."

Additional context

The result is same when using jest.useFakeTimers('legacy').

Environment

System:
    OS: Windows 10 10.0.19043
    CPU: (12) x64 AMD Ryzen 5 2600 Six-Core Processor            
  Binaries:
    Node: 14.17.6 - C:\Program Files\nodejs\node.EXE
    Yarn: 3.0.2 - ~\AppData\Roaming\npm\yarn.CMD
    npm: 7.23.0 - C:\Program Files\nodejs\npm.CMD
  npmPackages:
    jest: ^27.2.0 => 27.2.0

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:7
  • Comments:5

github_iconTop GitHub Comments

2reactions
nikopavlicacommented, Sep 20, 2021

This could be solvable if jest exposed the async counterparts of @sinonjs/fake-timers methods - the 'modern' implementation of fake timers.

Until that happens, it can be solved by using fake-timers directly:

const FakeTimers = require('@sinonjs/fake-timers');
const clock = FakeTimers.install();

it("delays twice in a function", async () => {
    async function f(): Promise<void> {
        await delay(100);
        await delay(100);
    }

    const p = f();
    await clock.runAllAsync(); // note the async method

    expect(await p).toBeUndefined();
});
1reaction
SimenBcommented, Apr 6, 2022

OP is fixed via #12572 by using advanceTimers: true. Available in https://github.com/facebook/jest/releases/tag/v28.0.0-alpha.8

Read more comments on GitHub >

github_iconTop Results From Across the Web

Jest: Timer and Promise don't work well. (setTimeout and ...
But calling await Promise.resolve() only seems to resolve one pending promise. In the real world, testing functions having multiple asynchronous ...
Read more >
Async Methods - Testing Library
Several utilities are provided for dealing with asynchronous code. These can be useful to wait for an element to appear or disappear in...
Read more >
Async/await - The Modern JavaScript Tutorial
Here's an example with a promise that resolves in 1 second: async function f() { let promise = new Promise((resolve, reject) => {...
Read more >
Asynchronous work - Jasmine Documentation
async functions implicitly return a promise. Jasmine will wait until the returned promise is either resolved or rejected before moving on to the...
Read more >
Asynchronous JavaScript - Discover three.js!
Fortunately, there's a function that allows us to perform a very simple asynchronous operation, called setTimeout . This method takes two arguments, a...
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