Timeout failure in asynchronous tests causing open handles
0 reputation · 10 May 2020, 06:00 UTC
0 reputation · 10 May 2020, 06:00 UTC
Jest utilizes a default timeout of 5000ms for tests. When an asynchronous operation—such as a Promise or a callback-based function—exceeds this threshold, the test runner triggers a failure. However, Jest does not natively cancel pending promises or terminate the underlying asynchronous operations once the timeout is reached.
In environments where tests trigger external network requests or long-running timers, a timeout failure may leave these operations active in the background. This can lead to "open handles" that prevent the test process from exiting cleanly, even after the test suite has reported a failure.
jest.setTimeout()What is the recommended strategy for ensuring that asynchronous operations are explicitly cancelled when a Jest timeout occurs? How can developers prevent these leaked handles from affecting subsequent tests in the same worker process?
Make the asynchronous work cancellable and ensure it is torn down in a test teardown so that, when Jest’s timeout fires, no background handles stay alive.
When a Jest test exceeds its timeout, the runner marks the test as failed but does not cancel the underlying Promise, timer, or network request. If that work holds resources (sockets, file descriptors, timers, etc.), those handles remain open and prevent the worker process from exiting cleanly.
Jest’s default timeout is 5000 ms; the test runner does not natively abort pending promises or timers. Open handles from unfinished async work are a known cause of the "Worker process failed to exit" warning and can leak into subsequent tests in the same worker.
done callback so Jest knows when the async work finishes.fetch, XMLHttpRequest) with an AbortController and abort it in an afterEach block.jest.useFakeTimers()) or explicitly clear them (clearTimeout, clearInterval) in teardown.afterEach or finally block.jest.setTimeout() if the operation genuinely needs more time.If you can share the type of asynchronous operation causing the timeout (e.g., network request, setInterval, database query), I can confirm whether the abort‑or‑clear approach above is sufficient or if a specific cleanup method is needed.
Use comments to ask for clarification. Post a solution as an answer.
34,775 reputation · 10 May 2020, 11:54 UTC
When a test exceeds Jest’s 5 s timeout, the runner marks it failed but the event loop still holds any pending setTimeout or setInterval callbacks. If those IDs are not manually cleared, they can keep the worker alive and surface as open‑handle warnings.
Instead of manually tracking each timer ID, enable fake timers once per test file:
beforeAll(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.runOnlyPendingTimers(); // flushes any remaining timers
jest.clearAllTimers();
});
Calling jest.runOnlyPendingTimers() forces all queued timers to execute immediately, ensuring they finish before the next test starts. This eliminates the need to remember every timer ID and guarantees no stray handles linger after a timeout failure.