Use Jasmine done() to Wait for Async Code in Unit Tests
Learn how Jasmine's done() callback pauses a spec until asynchronous work finishes, with a worked example and common failure modes.
27 Aug 2025, 11:26 UTC

Problem: A Jasmine spec that starts an asynchronous operation often finishes before the callback runs. Assertions execute against stale or undefined values, and the spec can pass even when the underlying operation fails.
Takeaway: Add done as a parameter to the it function. Jasmine waits for done() before marking the spec complete. Call it only after your assertions run inside the asynchronous callback.
How done() changes spec completion
Normally, Jasmine runs the function passed to it and marks the spec complete when that function returns. If the function starts an asynchronous operation and returns immediately, Jasmine does not wait for the operation to settle.
When the test function declares a done parameter, Jasmine treats the spec as pending until done() is invoked. If done() is never called, the spec fails after the default timeout. Passing an error to done(new Error('message')) fails the spec immediately.
Worked example: callback-based fetch
Assume a helper fetchUser(id, callback) that retrieves a user asynchronously and invokes callback(error, user).
it('fetches user profile asynchronously', function(done) {
fetchUser(42, function(error, user) {
if (error) {
done(error);
return;
}
expect(user.id).toBe(42);
expect(user.name).toBeDefined();
done();
});
});
Place done in the parameter list. Jasmine recognizes the parameter and suspends completion. The assertion block runs inside the callback, so it executes after the fetch returns. done() is called last, signaling success.
Promise-based alternative
Jasmine also supports returning a promise from the spec function. If your code uses promises, returning the promise is usually clearer than managing done:
it('fetches user profile with a promise', function() {
return fetchUser(42).then(function(user) {
expect(user.id).toBe(42);
});
});
Use done when the code under test only exposes callbacks or event listeners. Use returned promises when the API supports them.
Timeout configuration
Jasmine's default async timeout is 5000 milliseconds. If done() is not called within that window, the spec fails with a timeout error. Increase it for slow operations:
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
Set this in your support file or at the top of the spec file before the tests that need it. Do not raise the timeout globally just to hide slow tests; investigate the underlying delay first.
Common mistakes
- Forgetting
doneentirely. The spec returns immediately, assertions may run against undefined data, and Jasmine reports success or failure based on timing rather than behavior. - Calling
done()before assertions. Ifdone()runs before the assertion block, Jasmine completes the spec and never sees assertion failures that happen later. - Calling
done()more than once. Subsequent calls are generally ignored, but the pattern makes the flow harder to audit and can hide logic errors. - Placing assertions outside the callback. Code after the asynchronous call but outside its callback runs before the data arrives.
How to verify the behavior
- Open a terminal in the project root where Jasmine is installed.
- Run the suite:
npm testornpx jasmine. - Temporarily remove the
done()call from an async spec and run again. The spec should fail after the timeout interval. - Restore
done()and confirm the spec passes.
Required permissions: none beyond normal file execution. Risks: leaving a spec without done() can cause the suite to hang until timeout, slowing CI pipelines.
Limitations
done only applies to the spec function that declares it. It does not pause other specs or the overall suite. Clock mocking with jasmine.clock() can interfere with timer-based async code, so avoid combining them unless you understand the interaction. If the async operation never settles, the spec fails only after the timeout; there is no built-in way to cancel the pending operation from Jasmine.
Practical check: after adding done(), run the spec with a deliberately failing assertion inside the callback. The spec should fail on the assertion, not on a timeout, proving that Jasmine waited for the callback.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.