Understand the kind of waiting
A calculation can finish immediately on the current call stack. A response from another system arrives later. Repeating an empty loop until enough time passes does not make a program asynchronous; it occupies execution time.
const steps = [];
function calculate() {
steps.push("calculate");
return 20 * 3;
}
steps.push("before");
const result = calculate();
steps.push("after");
console.assert(result === 60);
console.assert(steps.join(",") === "before,calculate,after");
The caller waits for this synchronous call to return before executing its next statement. Join combines array entries into text with the chosen separator.
Guided comparison
Synchronous code is not inherently bad. Small calculations are naturally synchronous. The problem is making a long-running operation monopolize execution when other work should remain possible. A promise also does not automatically move CPU-heavy calculations to another thread.
Do not implement a delay with a loop checking the clock. In a browser that can prevent interaction and delay other callbacks. The next module represents later completion explicitly instead.
Independent exercise
Classify calculating a sum of five numbers, reading a remote response and formatting a label. Which produces its result from currently available values, and which depends on something arriving later?
Correction
The sum and formatting can complete synchronously with available data. A remote response is not immediately available; the program needs a way to represent completion or failure later. A large sum may still be synchronous but expensive, so synchronous and fast are not synonyms.
Review
Before introducing async syntax, identify what is actually unavailable now. Asynchronous control flow should express real waiting, not decorate every function unnecessarily.