How The Temporal Date-Time API Works Under the Hood
TL;DR
This guide explains under the hood clearly and practically: what it is, why it matters in 2026, and how to apply it step by step. You'll find core concepts, proven best practices, concrete data, trusted references, and a concise FAQ — everything you need in one focused place.
Key takeaways
- async/await is syntactic sugar over promises that makes asynchronous code read like synchronous code without blocking the thread.
- A closure is a function bundled with references to its surrounding lexical scope, letting it remember variables after the outer function returns.
- The event loop is single-threaded: it runs one task to completion, drains all microtasks, then optionally renders.
- Promise callbacks are microtasks and always run before the next macrotask such as a setTimeout callback.
- Break long tasks into smaller chunks and yield to the main thread to keep interfaces responsive.
This is a practical, up-to-date guide to Under the Hood — what it is, why it matters in 2026, and how to apply it in real projects. It is written for developers and founders who want clear answers and proven best practices, not filler.
Whether you're just starting out or leveling up, treat this as a working reference you can return to. Every section is built to be skimmed, applied, and shared.
What Is the Difference Between Microtasks and Macrotasks?
Macrotasks include setTimeout, setInterval, message events, and I/O callbacks. Microtasks include promise .then/.catch/.finally reactions, queueMicrotask, and await continuations. The defining rule: after each macrotask, the engine drains the microtask queue completely before the next macrotask or paint.
- Microtasks have higher priority and can starve rendering if you enqueue them in an unbounded loop.
- One
setTimeout(fn, 0)waits for the next macrotask turn, so it always runs after pending promises. awaitsplits a function: code after it resumes as a microtask.
Knowing this prevents subtle bugs where state appears to update in the wrong order, and explains why heavy promise chains can delay visual updates.
How Do You Optimize JavaScript Performance?
The biggest wins come from doing less on the main thread, not from clever micro-optimizations. Profile first with the browser Performance panel or Lighthouse, find the long tasks, then attack them. Optimize for the metric users feel: Interaction to Next Paint should stay under 200 ms.
High-impact techniques:
- Break long tasks into chunks and yield with
scheduler.yield()orsetTimeout. - Move CPU-heavy work to a Web Worker so the UI thread stays free.
- Debounce or throttle high-frequency events like
scroll,resize, andinput. - Defer non-critical scripts and code-split large bundles.
- Batch DOM reads and writes to avoid layout thrashing.
Measure again after each change; assumptions about hotspots are often wrong.
When Should You Use Promises vs Callbacks?
Callbacks are still appropriate for simple, synchronous-style APIs and for event handlers that fire many times. For one-shot asynchronous results, promises and async/await are almost always the better choice: they flatten nesting, propagate errors predictably, and compose with combinators.
Promises shine when coordinating multiple operations:
Promise.allwaits for everything and rejects fast on the first failure.Promise.allSettledwaits for all results regardless of failures.Promise.raceresolves with the first settled promise.Promise.anyresolves with the first success, ignoring rejections.
The classic "callback hell" of deeply nested handlers disappears once you return promises and chain or await them. Mixing both styles in one flow, however, is a frequent source of swallowed errors.
What Is a JavaScript Closure?
A closure is created every time a function is defined: the function keeps a live reference to the variables in the scope where it was declared, not where it is called. Because the inner function holds that reference, those variables survive after the outer function has returned. This is the mechanism behind data privacy, function factories, and stable callbacks.
A practical example is a counter:
function makeCounter() {
let count = 0;
return () => ++count;
}
const next = makeCounter();
next(); // 1
next(); // 2
The returned arrow function closes over count. Each makeCounter() call produces an independent count, so two counters never interfere. Closures are not copies of values; they share the actual binding, which is why loop variables declared with var historically caused surprises that let fixes.
How Does the JavaScript Event Loop Work?
JavaScript runs on a single thread with a call stack, a task (macrotask) queue, and a microtask queue. The engine takes one task, runs it to completion, then empties the entire microtask queue before doing anything else. Only after microtasks drain does the browser get a chance to render and pick the next task.
The ordering matters in practice:
- Synchronous code on the call stack runs first.
- Promise reactions and
queueMicrotaskcallbacks run next, fully draining. - Timers, I/O, and events run as later macrotasks.
This is why a Promise.resolve().then(...) always fires before a setTimeout(..., 0). Long synchronous work blocks the loop entirely, freezing input and rendering, which is the root cause of jank.
How Do ES Modules Differ From CommonJS?
ES modules (ESM) are the standardized module system defined by ECMAScript and supported natively in browsers and Node.js. CommonJS (CJS) is Node's original system built on require and module.exports. The differences are not just syntax; they affect loading and tooling.
- ESM uses static
import/export, enabling tree shaking and dead-code elimination. - CJS uses dynamic
require, resolved synchronously at runtime. - ESM bindings are live read-only views; CJS exports are copied values.
- ESM is asynchronous and supports top-level
await; CJS is synchronous.
New projects should default to ESM for better static analysis and smaller bundles. Use dynamic import() to load code on demand, which also returns a promise and integrates cleanly with async/await.
Under the Hood: Key Facts and Data
According to recent industry research and the official documentation linked below:
- async/await was standardized in ES2017 (ES8) and is supported by all modern browsers and Node.js 8+
- Stack Overflow's 2024 Developer Survey ranked JavaScript among the most commonly used languages, used by about 62% of developers
- Microtasks such as Promise callbacks drain completely after each task and before the next render, giving them priority over setTimeout callbacks
Quick-Reference Summary
A map of what this guide covers:
| Topic | What you'll learn |
|---|---|
| What Is the Difference Between Microtasks and Macrotasks? | Macrotasks include setTimeout, setInterval, message events, and I/O callbacks. |
| How Do You Optimize JavaScript Performance? | The biggest wins come from doing less on the main thread, not from clever micro-optimizations. |
| When Should You Use Promises vs Callbacks? | Callbacks are still appropriate for simple, synchronous-style APIs and for event handlers that fire many times. |
| What Is a JavaScript Closure? | A closure is created every time a function is defined |
| How Does the JavaScript Event Loop Work? | JavaScript runs on a single thread with a call stack, a task (macrotask) queue, and a microtask queue. |
| How Do ES Modules Differ From CommonJS? | ES modules (ESM) are the standardized module system defined by ECMAScript and supported natively in browsers and Node.js. |
How to Get Started with Under the Hood
A simple path that works:
- Learn the fundamentals of Under the Hood from primary sources, not just tutorials.
- Build one small, real project end to end.
- Get feedback, refactor, and add tests.
- Ship it publicly and document what you learned.
- Repeat with a slightly harder project each time.
Build It with a World-Class Full Stack Developer
Sandeep Kumar Chaudhary is a full stack world-class developer. If you want to turn this into a real, production-ready product, get in touch — message directly on WhatsApp at +9779802348957 for a fast, no-pressure consult.
You can also explore the projects already shipped to thousands of users, or start a conversation here.
Final Thoughts
async/await is syntactic sugar over promises that makes asynchronous code read like synchronous code without blocking the thread. The developers and teams who win in 2026 pair strong fundamentals with consistent shipping. Start small, stay curious, build in public, and revisit this guide as your skills grow.
Sources and Further Reading
Frequently Asked Questions
What is under the hood?
The biggest wins come from doing less on the main thread, not from clever micro-optimizations. Profile first with the browser Performance panel or Lighthouse, find the long tasks, then attack them. This guide covers under the hood end to end — core concepts, best practices, concrete data, and a step-by-step approach you can apply right away.
Why is `this` undefined in my callback?
Because `this` depends on how a function is called, not where it is defined. Passing a method as a callback detaches it from its object, so the implicit binding is lost. Fix it by using an arrow function, which captures `this` lexically, or by binding the method explicitly with `Function.prototype.bind`.
Why does a Promise callback run before setTimeout?
Promise callbacks are microtasks, and `setTimeout` callbacks are macrotasks. After each task finishes, the event loop drains the entire microtask queue before running the next macrotask or rendering. So a resolved promise's `.then` always executes before a `setTimeout(fn, 0)`, even when both are scheduled at the same moment.
Does async/await block the main thread?
No. `await` pauses only the surrounding async function and returns control to the event loop while waiting. The rest of your program keeps running, and the paused function resumes later as a microtask once the awaited promise settles. Blocking only happens if you run heavy synchronous computation, not from awaiting itself.
What is the difference between let, const, and var?
`var` is function-scoped and hoisted as `undefined`, which causes surprising bugs. `let` and `const` are block-scoped and live in a temporal dead zone until declared, throwing if accessed early. Use `const` by default for values that do not get reassigned, `let` when reassignment is needed, and avoid `var`.
Sandeep Kumar Chaudhary
Full Stack Software Developer· Nepal's SEO, AEO, GEO & AIO expert and share-market educator. More about me
