MediumJavascript. First: What is JavaScript itself? | by Chaitra KR | Medium
Sitemap

Javascript

5 min readAug 13, 2026

--

First: What is JavaScript itself?

JavaScript is a programming language — a set of instructions you write that a computer follows. It runs inside a browser (Chrome, Firefox, Safari) or in Node.js (a way to run it on a server/your computer).

You can think of it like a recipe. You write the steps, and the computer cooks.

Second: What does “single-threaded” mean?

Thread = a worker that does work. Imagine a kitchen with one chef. The chef can only handle one dish at a time — they can’t stir two pots simultaneously.

JavaScript is a kitchen with one chef. It can only do one thing at a time. This is called single-threaded.

Now here’s the crucial question: What if a dish takes 10 minutes to cook? If the chef just stands there watching it, the restaurant is dead — no other customers get served. That’s called blocking — the whole program freezes.

// This "blocks" — freezes everything for 2 seconds
const stopTime = Date.now() + 2000;
while (Date.now() < stopTime) {
// busy wait, do nothing
}
console.log('This takes 2 seconds to appear');

Nobody wants that. So JavaScript needs a way to not wait around.

Third: Sync vs Async — the whole trick

Synchronous (sync) = do this, wait for it to finish, then do the next thing. One at a time, in order.

Asynchronous (async) = start something, don’t wait for it, keep doing other things, check back later when it’s done.

// SYNCHRONOUS — in order, immediate
console.log('A');
console.log('B');
// Output: A, then B
// ASYNCHRONOUS — start, move on, come back
console.log('A');
setTimeout(() => console.log('B'), 2000); // "start a 2-sec timer, don't wait"
console.log('C');
// Output: A, C, then (2 sec later) B

The key insight: setTimeout doesn't freeze the program. It says "remind me in 2 seconds" and lets the code keep running. That's what asynchronous means. The program doesn't wait — it moves on and comes back later.

Fourth: So why do we need “callbacks, promises, async/await”?

Because of a problem: asynchronous tasks finish at unpredictable times, and the program has already moved on. So how do you say “when it’s finally done, do this?”

The simplest answer: a callback

A callback is just a function you hand to a task, to be called when the task finishes.

// "Do this work, and WHEN it's done (later), call this function"
waitThenTellMe(() => {
console.log('Now it is done!');
});

That’s it. That’s all a callback is. A “call me back later” function.

Why did promises exist? Because callbacks got messy.

If you need to do several things in sequence, each waiting on the last, callbacks nest deeper and deeper:

doStep1((result1) => {
doStep2(result1, (result2) => {
doStep3(result2, (result3) => {
// this is getting ugly
});
});
});

This is called “callback hell.” Hard to read, hard to fix errors.

A Promise is just a neater box that holds a future result. Instead of handing a callback to the task, the task hands you a Promise — a receipt saying “your result will come later, and I promise to tell you when.”

const receipt = doStep1(); // a Promise
// You can then say "when it's done, run this; if it fails, run that"
receipt
.then((result) => doStep2(result)) // chain nicely instead of nesting
.then((result) => doStep3(result));

So a Promise is a container for a value that will exist in the future. That’s the whole idea. It’s not magic — it’s just an organized way to say “something is coming later, and here’s how to react when it arrives.”

async/await = just cosmetic sugar

async/await is simply a prettier way to write Promises. It makes async code look like normal top-to-bottom code.

// Using promises (works, but a bit clunky)
function f() {
doStep1()
.then((r) => doStep2(r))
.then((r) => console.log(r));
}
// Using async/await (same thing, cleaner)
async function f() {
const r1 = await doStep1(); // wait for it -- but only HERE, not everywhere
const r2 = await doStep2(r1);
console.log(r2);
}

Important misunderstanding to clear up: await does NOT make the whole program wait. It only makes that specific function pause and wait. The rest of the program keeps running. That's the magic — you write it like it's synchronous, but underneath it's still async.

Fifth: The microstack / macrostack — WHERE do these live?

These are queues (waiting lines) that the JavaScript engine uses to decide what to do next.

Get Chaitra KR’s stories in your inbox

Join Medium for free to get updates from this writer.

Remember the one chef? Here’s how async actually works under the hood.

The kitchen has three places:

  1. The stove (call stack) — where the chef actively cooks one thing right now.
  2. Microwave queue (microtask queue) — fast tasks that should be done quickly, like Promises.
  3. Oven queue (macrotask queue) — slower tasks that can wait, like timers.

Here’s the chef’s rule (the event loop):

  1. Finish whatever is on the stove (current code).
  2. Empty the microwave queue completely.
  3. Cook ONE thing from the oven.
  4. Go back to step 2. Repeat forever.

Let’s trace it with a concrete example:

console.log('1');                            // stove: cooks now
setTimeout(() => console.log('2'), 0); // oven queue
Promise.resolve().then(() => console.log('3')); // microwave queue
console.log('4'); // stove: cooks now

Step by step:

  1. Stove cooks 1. → prints 1
  2. setTimeout → put it in the oven queue.
  3. The Promise’s .then → put it in the microwave queue.
  4. Stove cooks 4. → prints 4
  5. Stove is empty now. Chef empties the microwave → prints 3
  6. Chef cooks one thing from the oven → prints 2

Final output: 1, 4, 3, 2

The components:

  • Call stack — where synchronous functions execute, one at a time (LIFO).
  • Web APIs / Node APIs — background thread pool handling timers, network, I/O.
  • Microtask queue — promises, queueMicrotask, MutationObserver.
  • Macrotask queue (task queue) — setTimeout, setInterval, I/O callbacks, UI events.

The loop

1. Run all synchronous code in the call stack
2. When stack is empty, check microtask queue
3. Run ALL pending microtasks
4. Take ONE macrotask from the macrotask queue
5. Repeat: drain microtasks again → next macrotask → ...

The single most important big picture

Everything — callbacks, promises, async/await, microstacks, macrostacks — exists to solve one problem: JavaScript has one worker, and slow tasks must not freeze it.

Every concept is just a different tool for handling that:

  • Callback — “call me when you’re done” (the raw idea)
  • Promise — a tidy box for a future result, so you don’t nest callbacks
  • async/await — pretty typing for working with promises
  • Micro/Macro stacks — the actual queues that decide execution order
  • Event loop — the chef continuously checking the queues

--

--

Chaitra KR
Chaitra KR

Written by Chaitra KR

Quality analyst at Thoughtworks.