Asynchronous Programming
JavaScript Promises
Function Execution Order
Callback Functions
Synchronous vs Asynchronous

Forcing a function to wait until another function is complete

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In JavaScript, you usually do not "force" one function to wait by blocking the thread. Instead, you structure the code so the second function runs only after the first asynchronous operation has finished, typically with promises and await.

That distinction matters because JavaScript's event loop is designed around non-blocking execution. If you try to simulate waiting with busy loops or timing hacks, you make the program slower and less reliable without actually solving the coordination problem correctly.

If the First Function Is Synchronous

For synchronous functions, nothing special is required. JavaScript already executes them in order:

javascript
1function stepOne() {
2  console.log("step one");
3}
4
5function stepTwo() {
6  console.log("step two");
7}
8
9stepOne();
10stepTwo();

stepTwo runs only after stepOne finishes.

The problem appears when stepOne starts asynchronous work such as a timer, network request, or file operation.

Return a Promise and await It

Modern JavaScript solves this with promises:

javascript
1function stepOne() {
2  return new Promise(resolve => {
3    setTimeout(() => {
4      console.log("step one finished");
5      resolve();
6    }, 1000);
7  });
8}
9
10async function run() {
11  await stepOne();
12  console.log("step two starts now");
13}
14
15run();

await does not block the whole JavaScript runtime. It pauses the async function until the promise settles, which is exactly what you want for ordered async logic.

Chain Promises When You Cannot Use await

If you are in code that is not async, promise chaining works too:

javascript
1function stepOne() {
2  return fetch("https://example.com/data");
3}
4
5stepOne()
6  .then(response => response.text())
7  .then(text => {
8    console.log("step one complete");
9    console.log(text);
10  })
11  .catch(error => {
12    console.error("request failed", error);
13  });

This is still sequencing. The follow-up logic runs only after the first asynchronous operation resolves.

Callbacks Are the Older Pattern

Older APIs often use callbacks:

javascript
1function stepOne(done) {
2  setTimeout(() => {
3    console.log("step one finished");
4    done();
5  }, 1000);
6}
7
8stepOne(() => {
9  console.log("step two starts now");
10});

This works, but deeply nested callbacks become hard to read. Promises and async functions are usually cleaner.

Do Not Use Fake Waiting

Developers sometimes try patterns like:

  • 'setTimeout(stepTwo, 1000) and hope step one is done in time'
  • busy loops that block the thread
  • global flags checked repeatedly in intervals

These are all weak substitutes for actual completion signals. Time-based guesses fail when the first task is slower than expected, and blocking loops freeze the UI or event loop.

The right design is always the same: the first function should expose when it is done, and the second function should start from that signal.

A Practical Rule

If function B depends on the result of function A:

  1. make function A return a promise or accept a callback
  2. start function B only after A resolves or invokes the callback

That makes the dependency explicit and keeps your code honest about what is asynchronous.

Common Pitfalls

  • Trying to block JavaScript instead of using a promise or callback.
  • Forgetting to return the promise from the first function, which makes await useless.
  • Using timers as guesses for completion instead of listening to the real completion event.
  • Mixing callback and promise styles carelessly and making control flow harder to follow.

Summary

  • Synchronous functions already run in order without extra work.
  • For asynchronous functions, return a promise and use await when possible.
  • Promise chaining and callbacks are alternative sequencing mechanisms.
  • Do not use busy waits or guessed delays to coordinate async code.
  • The correct pattern is to run the second function from the first function's real completion signal.

Course illustration
Course illustration

All Rights Reserved.