⚠️ Async Functions Are Not Mockable
⚠️ Async Functions Are Not Mockable
Async functions are convenient in JavaScript, but they are incompatible with mockable Promises like MockChain.
This post explains why async functions break mockability and how to preserve control over promise chains.
❌ Why Async Functions Break Mockability
An async function always returns a native Promise, even if you return another Promise (as a MockChain) inside it.
async function example() {
const chain = MockChain.resolve(42);
return chain; // You think you're returning a MockChain...
}
const result = example();
console.log(result instanceof MockChain); // ❌ false
Why this happens: ECMAScript’s Promise assimilation wraps any thenable in a new native Promise, so the original instance is lost.
Consequences:
- Custom subclass methods (
.mock(),.fail(),.root,.parent) disappear. - Any attached metadata (
promise.parent, etc.) is not preserved.
Even returning a MockChain or attaching properties to the inner promise doesn’t survive the async wrapper.
⚙️ Example of Broken Mockability
async function doFetch(url, opts, autostart = true) {
const chain = MockChain.fromResponse(url, opts, autostart).then(res => res.json());
return chain; // ❌ Lost after return
}
const promise = doFetch(undefined, undefined, false);
console.log(promise instanceof MockChain); // ❌ -> false
The returned object is a native Promise with no .mock() — the MockChain identity is gone.
✅ Correct Pattern for Mockable Async Logic
Avoid async for functions that return MockChain. Use plain functions instead:
// A plain function: note that the `async` keyword is intentionally omitted
function doFetch(url, opts, autostart = true) {
const chain = MockChain.fromResponse(url, opts, autostart).then(res => res.json());
return chain; // ✅ still mockable
}
const chain = doFetch(undefined, undefined, false);
// Mock the root of the chain to simulate a response
chain.root.mock(new Response(JSON.stringify({ ok: true })));
// Consume the chain as usual
chain.then(data => console.log(data));
// → { ok: true }
This ensures the caller receives the original MockChain, preserving full mockability.
🧠 Key Takeaways
asyncfunctions always wrap returned values in native Promises.- Any Promise subclass (like
MockChain) or attached properties are lost. - To preserve mockable promises, use regular functions when returning
MockChaininstances.
Rule of thumb: Async functions are excellent for orchestration, but not for controlled or mockable asynchronous flows.
Comments
Post a Comment