Decode: sync-over-async and the dropped token
This module is different. You’ve learned the idioms; now you decode the actual review comments Rai received on Fusion PRs, so that the next time one lands, you already know what it means and how to write the thing right the first time. We start with the single most-repeated correctness note.
The comment (Mustafa Aleem, on PR #491 and again on #508):
“use
awaitinstead of.GetAwaiter().GetResult(); make this method async as well.” “Sync over async again, when it should be awaited.” “‘.GetAwaiter().GetResult()’ was reintroduced and should be removed.” (left twice)
And on the token:
“don’t have
= default, cancellationTokens must always be provided.”
What he meant. Two distinct rules, both invisible to a JS developer because JS can’t express the mistake. First, sync-over-async: calling .Result or .GetAwaiter().GetResult() on a Task blocks the current thread until the IO finishes. Under load that starves the thread pool and can deadlock, and it silently discards cancellation. The fix is never to bridge; await the task and let async spread up the call chain (Module 7 covered the mechanism). Second, the required token: a CancellationToken cancellationToken with = default lets every caller quietly drop cancellation, so an abandoned request keeps running a Databricks query for nothing. Making the parameter required forces the token to be threaded from the endpoint all the way down.
// What Mustafa flagged: blocks a thread, discards cancellation.
var summary = _repo.LoadRevertSummary(key).GetAwaiter().GetResult();
// The fix: await, make the caller async, thread a required token down.
public async Task<FlightRevertSummary?> LoadRevertSummary(
string key, CancellationToken cancellationToken)
{
return await _repo.LoadRevertSummary(key, cancellationToken);
}Try it yourself
Restate Mustafa's note as a rule
Turn the review comment into the principle you'll carry into design.
Mustafa wrote, more than once, "use await instead of .GetAwaiter().GetResult()" and "cancellationTokens must always be provided." State the two rules those comments encode, and why each matters in .NET but not in JS.
Reveal answer
Rule 1: never block on async (no .Result, .Wait(), .GetAwaiter().GetResult()); await it and make the caller async, because blocking holds a thread-pool thread and can deadlock. Rule 2: make CancellationToken a required parameter (no = default) and thread it to the IO, so a cancelled request actually stops work. Neither exists in JS: it's single-threaded so 'sync over async' isn't expressible, and cancellation is opt-in via AbortSignal rather than a threaded contract.
Match the fix to the comment
Mustafa left the comment: "Sync over async again, when it should be awaited." Which change resolves it?
Show answer
Correct answer: D — Replace .GetAwaiter().GetResult() with await and make the method async
The fix for sync-over-async is to await the call and let async cascade up: change the body to await, mark the method async, and return Task/Task<T>. Adding = default to a token is the opposite of what he asks elsewhere.