Decision Guide: Jasmine Done Callback vs Async/Await for Asynchronous Tests
Guide to choosing Jasmine's done callback or async/await for asynchronous tests, with a comparison table, trade‑offs, and runnable examples.
05 Jan 2026, 16:23 UTC

Decision and Constraints
When writing Jasmine specs that involve asynchronous work, the test runner must know when the operation finishes. If the runner proceeds too early, assertions may never run or may run against stale state, leading to false passes or unpredictable failures. The choice is between the done callback (the traditional way to signal completion) and returning a Promise (enabling async/await). The decision hinges on the shape of the asynchronous API you are testing and the Jasmine version you are using.
Comparison of Options
| Aspect | Done Callback | Async/Await (Promise) |
|---|---|---|
| When to use | APIs that use callbacks or events (e.g., setTimeout, DOM events, Node‑style (err, data) functions) |
Functions that return a Promise (e.g., fetch, database queries, Promise‑wrapped libraries) |
| Completion signal | Call done() for success; pass an error or call done(err) to fail |
Return a Promise; Jasmine resolves/rejects it automatically |
| Error handling | Must catch errors and signal them via done(err) (or try/catch around the callback) |
Any thrown error or rejected promise fails the spec automatically |
| Readability | Chaining multiple async steps can nest callbacks | Linear await steps improve flow |
| Risk | Forgotten done() causes the spec to hang until the global timeout |
Missing await or not returning a Promise can let the spec finish too early |
Trade‑offs
Readability and Maintenance
The done style often leads to nested callbacks when several asynchronous steps must be sequenced. async/await turns those chains into a straightforward, top‑to‑bottom sequence, which is easier to follow and modify.
Error Propagation
In an async function, any exception or rejected promise propagates to Jasmine and marks the spec as failed. With the done callback, an uncaught exception inside the callback will not be caught by Jasmine; the spec will hang until the timeout expires, making debugging harder.
Timeout Management
Both patterns respect jasmine.DEFAULT_TIMEOUT_INTERVAL (default 5 seconds). If an asynchronous operation may exceed this, increase the interval in a beforeAll block or configure it globally for the suite.
Implementation and Validation
Using the Done Callback (Callback‑Based APIs)
This pattern is appropriate when the code under test does not return a Promise.
// Run in any Jasmine version that supports the done callback
describe('Timer‑based API', function() {
it('should complete after the delay', function(done) {
setTimeout(function() {
expect(true).toBeTrue();
done(); // signal success
}, 10);
});
});
Validation: Temporarily remove the done() call. The spec will not finish immediately; after the default timeout (or a custom interval) Jasmine will report a timeout error, confirming that the runner was waiting for the signal.
Using Async/Await (Promise‑Based APIs)
Use this when the function under test returns a Promise. Mark the it function as async.
// Run in Jasmine versions that support returning a promise from it
// (consult your release notes; many recent versions do)
describe('Promise‑based API', function() {
it('should resolve with expected value', async function() {
const value = await Promise.resolve('ok');
expect(value).toBe('ok');
});
});
Validation: Change the resolved value to a rejected promise, e.g., await Promise.reject(new Error('fail')). The spec should fail immediately with the rejection reason, without needing any explicit done.fail call.
Critical Constraints and Risks
- Do not mix patterns in the same
itblock: avoid declaring a parameterdonewhile also returning a Promise or marking the functionasync. Jasmine cannot determine which signal to trust, leading to hangs or unexpected failures. - Forgotten
await: calling an async function withoutawaitreturns a Promise that is ignored, so the spec may finish before the asynchronous work runs, producing false positives. - Timeout adjustment: for slower tests, set the interval in a
beforeAllblock:beforeAll(function() { jasmine.DEFAULT_TIMEOUT_INTERVAL = 15000; // 15 seconds });
Practical Decision Checklist
- Does the asynchronous API return a Promise? → Use async/await.
- Does the API use a callback or event emitter? → Use done().
- Are you on an older Jasmine release that does not support returning a promise from
it? → Use done(). - Is the spec hanging? → Check for missing done() or missing await.
- Does the test need more than the default timeout? → Adjust jasmine.DEFAULT_TIMEOUT_INTERVAL.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.