JavaScript
Promises
Debugging
Repl.it
Async

Why am I seeing Promise pending from this code on repl.it?

Master System Design with Codemia

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

Introduction

Seeing Promise pending output usually means you logged a promise object before it resolved. This is common on Replit and Node environments where asynchronous operations finish after your current line executes. The fix is not to avoid promises, but to consume them correctly with await or .then.

Why a Promise Shows as Pending

A promise has three states: pending, fulfilled, and rejected. When you run asynchronous code, JavaScript returns the promise immediately, then completes the operation later.

javascript
1function waitAndReturn() {
2  return new Promise((resolve) => {
3    setTimeout(() => resolve("done"), 500);
4  });
5}
6
7const result = waitAndReturn();
8console.log(result); // Promise pending

console.log runs before the timer resolves, so you see a pending promise object.

If you log after awaiting, you get the final value:

javascript
1async function main() {
2  const result = await waitAndReturn();
3  console.log(result); // done
4}
5
6main();

The key idea is timing, not an error in promises themselves.

Correct Consumption Patterns on Replit

On Replit with Node.js, you can use either an async entry function or top-level await in supported runtime modes.

Pattern one, async main:

javascript
1async function fetchJson(url) {
2  const response = await fetch(url);
3  if (!response.ok) {
4    throw new Error(`HTTP ${response.status}`);
5  }
6  return response.json();
7}
8
9async function main() {
10  try {
11    const data = await fetchJson("https://jsonplaceholder.typicode.com/todos/1");
12    console.log(data.title);
13  } catch (err) {
14    console.error("Request failed:", err.message);
15  }
16}
17
18main();

Pattern two, .then and .catch:

javascript
1fetch("https://jsonplaceholder.typicode.com/todos/1")
2  .then((res) => {
3    if (!res.ok) throw new Error(`HTTP ${res.status}`);
4    return res.json();
5  })
6  .then((data) => {
7    console.log(data.title);
8  })
9  .catch((err) => {
10    console.error(err.message);
11  });

Both are valid. Use one style consistently in each function to reduce mistakes.

Understand Event Loop Ordering

Promise callbacks run in the microtask queue after current synchronous code completes.

javascript
1console.log("A");
2
3Promise.resolve().then(() => {
4  console.log("B");
5});
6
7console.log("C");

Output order is A, C, then B. Knowing this explains why pending logs appear before resolved values.

On Replit, this behavior is identical to standard Node semantics. The platform is usually not the problem.

Practical Debugging Techniques

When debugging async flows, log both promise creation and resolved values.

javascript
1async function run() {
2  const p = waitAndReturn();
3  console.log("created promise", p);
4
5  const value = await p;
6  console.log("resolved value", value);
7}
8
9run();

For error diagnosis, always attach handling:

javascript
process.on("unhandledRejection", (reason) => {
  console.error("Unhandled rejection:", reason);
});

This helps when a promise rejects and nothing catches it.

If output appears inconsistent, verify you are not forgetting await inside loops:

javascript
1async function badLoop() {
2  const items = [1, 2, 3];
3  items.forEach(async (n) => {
4    await waitAndReturn();
5    console.log(n);
6  });
7  console.log("loop finished");
8}

forEach does not await async callbacks. Prefer for...of with await for ordered execution.

Return Resolved Values from Async Functions

If you need caller code to receive final data, return the awaited value from an async function instead of logging inside inner functions only.

javascript
1async function getTitle() {
2  const data = await fetchJson(\"https://jsonplaceholder.typicode.com/todos/1\");
3  return data.title;
4}
5
6getTitle().then((title) => console.log(\"title:\", title));

This makes control flow explicit and easier to test.

Common Pitfalls

A common mistake is writing const x = asyncFn(); console.log(x) and expecting resolved data. That logs the promise object, not the final value.

Another issue is mixing await and .then in the same chain without clear error handling. Keep one style per function and always include try/catch or .catch.

Developers also assume Replit causes pending output by itself. In most cases the root cause is standard event loop timing or a missing await.

Summary

  • Pending output means the asynchronous operation has not resolved yet.
  • Use await or .then to consume the resolved value.
  • Add explicit error handling for rejected promises.
  • Learn event loop order to interpret log timing correctly.
  • Prefer consistent async patterns to reduce debugging time.

Course illustration
Course illustration

All Rights Reserved.