Is Javascript Synchronous OR Asynchronous?

At its core, JavaScript is a synchronous, blocking and single-threaded programming language. This means the JavaScript engine has only one call stack and executes your code sequentially – one line at a time, in order. If a specific task takes a long time to complete, it will block the execution of any code following it.

However, JavaScript can behave asynchronously because of the runtime environment (like a web browser or Node.js) that surrounds it.

How JavaScript Handles Asynchronicity

While the JavaScript language itself cannot do two things at once, it offloads time-consuming tasks to the environment:

ComponentRole in the Ecosystem
JavaScript EngineSynchronous. Handles the single call stack and executes the actual code instructions.
Runtime Environment (Browser/Node)Asynchronous. Provides background tools like Web APIs (e.g., fetch(), setTimeout) to handle long operations without freezing the main thread.
The Event LoopThe Coordinator. Constantly watches the call stack. Once the synchronous code finishes and the stack is empty, it pushes the completed asynchronous callbacks back into the stack to be executed.

Direct Comparison

// 1. SYNCHRONOUS EXAMPLE
console.log("First");
console.log("Second"); // "Second" absolutely waits for "First" to finish

// 2. ASYNCHRONOUS EXAMPLE
console.log("First");

setTimeout(() => {
  console.log("Second"); // Sent to a browser timer API; does not block the next line
}, 1000);

console.log("Third");

// Output:
// First
// Third
// Second

By using native features like Callbacks, Promises and async/await, developers write code that handles modern web requirements – like loading data from servers – without freezing user interfaces


The key misconception is this:

JavaScript does NOT execute normal function calls asynchronously.

Your code is completely synchronous.

So when JS reaches:

outer();

it must finish executing outer() before moving to the next line:

console.log(name);

That’s why the output is:

Jane
John
Abhilash

1. Let’s execute it step by step

Your code:

const name = "Abhilash";

function outer() {
  const name = "John";

  if (true) {
    const name = "Jane";
    console.log(name);
  }

  console.log(name);
}

outer();

console.log(name);

JavaScript starts at the top:

const name = "Abhilash";

Global name is:

name = "Abhilash"

Then JS encounters the function declaration:

function outer() { ... }

The function is defined, but its body is not executed yet.

Then:

outer();

This means:

Call outer() and execute its body now.


2. What happens inside outer()?

First:

const name = "John";

Now there is a new local variable:

Global scope
name = "Abhilash"

outer() scope
name = "John"

Then:

if (true) {
  const name = "Jane";
  console.log(name);
}

The if block creates another scope:

Global scope
name = "Abhilash"

outer scope
name = "John"

if block scope
name = "Jane"

So:

console.log(name);

looks for name in the nearest scope.

It finds:

Jane

So:

Jane

is printed.


3. After the if block ends

The block-scoped variable:

const name = "Jane";

is no longer accessible.

We’re back in the outer() function’s scope:

outer scope
name = "John"

So:

console.log(name);

prints:

John

Then outer() finishes.

Only now does JavaScript continue to:

console.log(name);

We’re back in the global scope:

name = "Abhilash"

So it prints:

Abhilash

Therefore:

Jane
John
Abhilash

4. Why doesn’t JavaScript “move fast” to the next line?

This is the important part.

You said:

“because JavaScript has asynchronous execution”

That isn’t quite correct.

JavaScript supports asynchronous operations, but ordinary JavaScript execution is synchronous.

Consider:

function hello() {
  console.log("Hello");
}

hello();

console.log("World");

The output is:

Hello
World

5. Think about the call stack

This is the easiest mental model for interviews.

When JS sees:

outer();

the function is pushed onto the call stack.

Call Stack

outer()
------
global code

Then JavaScript executes:

const name = "John";

Then the if block:

console.log(name);

prints:

Jane

Then:

console.log(name);

prints:

John

Then outer() finishes and is removed from the stack:

Call Stack

global code

Now JavaScript executes:

console.log(name);

and prints:

Abhilash

6. Where does asynchronous behavior actually happen?

Consider:

console.log("A");

setTimeout(() => {
  console.log("B");
}, 0);

console.log("C");

Output:

A
C
B

Here you get asynchronous behavior.

Why?

console.log("A")
      ↓
A

setTimeout(...)
      ↓
schedule callback

console.log("C")
      ↓
C

current synchronous code finishes
      ↓
event loop
      ↓
callback runs
      ↓
B

So:

Normal function call
    ↓
Synchronous

setTimeout / Promise / fetch / async I/O
    ↓
Can involve asynchronous execution

7. fetch() is a good example related to your React questions

console.log("A");

fetch("/api/users")
  .then(() => {
    console.log("B");
  });

console.log("C");

You’ll normally see:

A
C
B

fetch() starts an asynchronous operation. JavaScript doesn’t wait for the network request to finish before continuing with:

console.log("C");

That’s different from:

outer();
console.log(name);

because outer() is just an ordinary synchronous function.


8. Your outer() example is also demonstrating lexical scoping

There are three different name variables:

const name = "Abhilash"; // global

function outer() {
  const name = "John"; // outer scope

  if (true) {
    const name = "Jane"; // block scope
  }
}

They’re not the same variable.

The inner declarations shadow the outer declarations.

JavaScript resolves:

console.log(name);

by looking outward:

Current scope
    ↓
Parent scope
    ↓
Parent scope
    ↓
Global scope

So inside the if:

Jane found

Inside outer():

John found

Outside outer():

Abhilash found

The most important correction to remember

Don’t think:

“JavaScript is asynchronous, so it doesn’t wait for functions.”

Think:

“JavaScript executes normal code synchronously. It waits for an ordinary function call to finish. Asynchronous APIs such as timers, network requests, and Promises allow work to be completed later via the event loop.”

That’s a very important distinction for understanding React, promises, async/await, fetch, callbacks and the JavaScript event loop.