⚡ Why This Topic Matters
Grasping the difference between synchronous, asynchronous, blocking, and non-blocking code is core to understanding Node.js — and it underpins everything else in this course.
🔒 Synchronous (Blocking) Code
<!-- Infographic: Synchronous Code Execution — same lane UI as async, but strictly sequential, 16:9, responsive, label-only -->
<div class="infographic-syncexec">
<style>
.infographic-syncexec {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
font-family: 'Inter', system-ui, sans-serif;
color: var(--ink);
background: var(--paper);
width: 100%;
max-width: 960px;
margin: 0 auto;
box-sizing: border-box;
aspect-ratio: 16 / 9;
display: flex;
flex-direction: column;
overflow: hidden;
container-type: inline-size;
container-name: synccard;
}
.infographic-syncexec[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
}
.infographic-syncexec * { box-sizing: border-box; }
.sy-stage {
flex: 1;
display: flex;
flex-direction: column;
align-items: stretch;
justify-content: center;
gap: clamp(0.5rem, 4cqw, 1.6rem);
min-height: 0;
min-width: 0;
padding: clamp(0.5rem, 4cqw, 2rem) clamp(0.8rem, 6cqw, 3rem);
}
.sy-lane {
display: flex;
align-items: center;
gap: clamp(0.4rem, 2.4cqw, 1rem);
min-width: 0;
}
.sy-node {
display: flex;
align-items: center;
gap: clamp(0.25rem, 1.2cqw, 0.55rem);
flex: 0 0 auto;
min-width: 0;
}
.sy-node-box {
width: clamp(28px, 8cqw, 56px);
height: clamp(28px, 8cqw, 56px);
border-radius: 10px;
border: 2px dashed var(--line);
background: var(--paper-alt);
display: flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
position: relative;
transition: border-color 0.2s, background 0.2s;
}
.sy-node-box svg { width: clamp(13px, 3.6cqw, 26px); height: clamp(13px, 3.6cqw, 26px); stroke: var(--ink-soft); transition: stroke 0.2s; }
.sy-node-box::before {
content: "";
position: absolute;
inset: -5px;
border-radius: 13px;
border: 1.5px solid var(--accent);
opacity: 0;
}
.sy-node-label {
font-size: clamp(0.44rem, 1.7cqw, 0.85rem);
font-weight: 600;
color: var(--ink-soft);
white-space: nowrap;
transition: color 0.2s;
}
/* only one lane "active" (dashed accent border, ring pulse, dark label) at a time, in strict order */
.sy-lane.l1 .sy-node-box { animation: sy-box-1 4.5s ease-in-out infinite; }
.sy-lane.l2 .sy-node-box { animation: sy-box-2 4.5s ease-in-out infinite; }
.sy-lane.l3 .sy-node-box { animation: sy-box-3 4.5s ease-in-out infinite; }
.sy-lane.l1 .sy-node-box svg { animation: sy-icon-1 4.5s ease-in-out infinite; }
.sy-lane.l2 .sy-node-box svg { animation: sy-icon-2 4.5s ease-in-out infinite; }
.sy-lane.l3 .sy-node-box svg { animation: sy-icon-3 4.5s ease-in-out infinite; }
.sy-lane.l1 .sy-node-label { animation: sy-label-1 4.5s ease-in-out infinite; }
.sy-lane.l2 .sy-node-label { animation: sy-label-2 4.5s ease-in-out infinite; }
.sy-lane.l3 .sy-node-label { animation: sy-label-3 4.5s ease-in-out infinite; }
.sy-lane.l1 .sy-node-box::before { animation: sy-ring-1 4.5s ease-in-out infinite; }
.sy-lane.l2 .sy-node-box::before { animation: sy-ring-2 4.5s ease-in-out infinite; }
.sy-lane.l3 .sy-node-box::before { animation: sy-ring-3 4.5s ease-in-out infinite; }
@keyframes sy-box-1 {
0%, 2% { border-color: var(--line); background: var(--paper-alt); }
6%, 27% { border-color: var(--accent); background: var(--accent-soft); }
31%, 100%{ border-color: var(--line); background: var(--paper-alt); }
}
@keyframes sy-box-2 {
0%, 35% { border-color: var(--line); background: var(--paper-alt); }
39%, 60% { border-color: var(--accent); background: var(--accent-soft); }
64%, 100%{ border-color: var(--line); background: var(--paper-alt); }
}
@keyframes sy-box-3 {
0%, 68% { border-color: var(--line); background: var(--paper-alt); }
72%, 93% { border-color: var(--accent); background: var(--accent-soft); }
97%, 100%{ border-color: var(--line); background: var(--paper-alt); }
}
@keyframes sy-icon-1 {
0%, 2% { stroke: var(--ink-soft); }
6%, 27% { stroke: var(--accent); }
31%, 100%{ stroke: var(--ink-soft); }
}
@keyframes sy-icon-2 {
0%, 35% { stroke: var(--ink-soft); }
39%, 60% { stroke: var(--accent); }
64%, 100%{ stroke: var(--ink-soft); }
}
@keyframes sy-icon-3 {
0%, 68% { stroke: var(--ink-soft); }
72%, 93% { stroke: var(--accent); }
97%, 100%{ stroke: var(--ink-soft); }
}
@keyframes sy-label-1 {
0%, 2% { color: var(--ink-soft); }
6%, 27% { color: var(--ink); }
31%, 100%{ color: var(--ink-soft); }
}
@keyframes sy-label-2 {
0%, 35% { color: var(--ink-soft); }
39%, 60% { color: var(--ink); }
64%, 100%{ color: var(--ink-soft); }
}
@keyframes sy-label-3 {
0%, 68% { color: var(--ink-soft); }
72%, 93% { color: var(--ink); }
97%, 100%{ color: var(--ink-soft); }
}
@keyframes sy-ring-1 {
6% { opacity: 0.55; transform: scale(0.92); }
24% { opacity: 0; transform: scale(1.18); }
100% { opacity: 0; }
}
@keyframes sy-ring-2 {
39% { opacity: 0.55; transform: scale(0.92); }
57% { opacity: 0; transform: scale(1.18); }
100% { opacity: 0; }
}
@keyframes sy-ring-3 {
72% { opacity: 0.55; transform: scale(0.92); }
90% { opacity: 0; transform: scale(1.18); }
100% { opacity: 0; }
}
/* track: fills sequentially — no overlap, one lane at a time */
.sy-track {
position: relative;
flex: 1 1 auto;
min-width: 0;
height: clamp(7px, 2.2cqw, 14px);
border-radius: 999px;
background: var(--paper-alt);
border: 1px solid var(--line);
overflow: hidden;
}
.sy-fill {
position: absolute; left: 0; top: 0; bottom: 0;
width: 0%;
background: var(--accent);
border-radius: 999px;
}
.sy-lane.l1 .sy-fill { animation: sy-fill-1 4.5s ease-in-out infinite; }
.sy-lane.l2 .sy-fill { animation: sy-fill-2 4.5s ease-in-out infinite; }
.sy-lane.l3 .sy-fill { animation: sy-fill-3 4.5s ease-in-out infinite; }
@keyframes sy-fill-1 {
0%, 2% { width: 0%; }
27% { width: 100%; }
31% { width: 100%; }
35%, 100%{ width: 0%; }
}
@keyframes sy-fill-2 {
0%, 35% { width: 0%; }
60% { width: 100%; }
64% { width: 100%; }
68%, 100%{ width: 0%; }
}
@keyframes sy-fill-3 {
0%, 68% { width: 0%; }
93% { width: 100%; }
97% { width: 100%; }
100% { width: 0%; }
}
@container synccard (max-width: 380px) {
.sy-node-label { display: none; }
.sy-stage { padding: 0.6rem; gap: 0.7rem; }
}
@media (prefers-reduced-motion: reduce) {
.infographic-syncexec *, .infographic-syncexec *::before, .infographic-syncexec *::after {
animation-duration: 0.001ms !important;
}
}
</style>
<div class="sy-stage">
<div class="sy-lane l1">
<div class="sy-node">
<div class="sy-node-box">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m18 16 4-4-4-4"/><path d="m6 8-4 4 4 4"/><path d="m14.5 4-5 16"/></svg>
</div>
<div class="sy-node-label">Task A</div>
</div>
<div class="sy-track"><div class="sy-fill"></div></div>
</div>
<div class="sy-lane l2">
<div class="sy-node">
<div class="sy-node-box">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m18 16 4-4-4-4"/><path d="m6 8-4 4 4 4"/><path d="m14.5 4-5 16"/></svg>
</div>
<div class="sy-node-label">Task B</div>
</div>
<div class="sy-track"><div class="sy-fill"></div></div>
</div>
<div class="sy-lane l3">
<div class="sy-node">
<div class="sy-node-box">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m18 16 4-4-4-4"/><path d="m6 8-4 4 4 4"/><path d="m14.5 4-5 16"/></svg>
</div>
<div class="sy-node-label">Task C</div>
</div>
<div class="sy-track"><div class="sy-fill"></div></div>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-syncexec')
|| document.querySelector('.infographic-syncexec');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
})();
</script>📖 What It Means
Code is executed line by line, one statement after another.
Each line waits for the previous line to finish before running.
💻 Example (from earlier notes)
const fs = require('fs');
const textIn = fs.readFileSync('./txt/input.txt', 'utf8');
console.log(textIn);Here:
First, the
fsmodule is loaded.Then, the file is read.
Only after that finishes, the result is logged.
⚠️ The Problem
This is called blocking code because a slow operation (like reading a large file) blocks everything else from running until it completes.
This becomes a serious issue in Node.js due to how Node is designed (explained below).
🚀 Asynchronous (Non-Blocking) Code
<!-- Infographic: Asynchronous Code Execution — tasks run concurrently, 16:9, responsive, label-only, animated -->
<div class="infographic-asyncexec">
<style>
.infographic-asyncexec {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
font-family: 'Inter', system-ui, sans-serif;
color: var(--ink);
background: var(--paper);
width: 100%;
max-width: 960px;
margin: 0 auto;
box-sizing: border-box;
aspect-ratio: 16 / 9;
display: flex;
flex-direction: column;
overflow: hidden;
container-type: inline-size;
container-name: asynccard;
}
.infographic-asyncexec[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
}
.infographic-asyncexec * { box-sizing: border-box; }
.as-stage {
flex: 1;
display: flex;
flex-direction: column;
align-items: stretch;
justify-content: center;
gap: clamp(0.5rem, 4cqw, 1.6rem);
min-height: 0;
min-width: 0;
padding: clamp(0.5rem, 4cqw, 2rem) clamp(0.8rem, 6cqw, 3rem);
}
.as-lane {
display: flex;
align-items: center;
gap: clamp(0.4rem, 2.4cqw, 1rem);
min-width: 0;
}
.as-node {
display: flex;
align-items: center;
gap: clamp(0.25rem, 1.2cqw, 0.55rem);
flex: 0 0 auto;
min-width: 0;
}
.as-node-box {
width: clamp(28px, 8cqw, 56px);
height: clamp(28px, 8cqw, 56px);
border-radius: 10px;
border: 2px dashed var(--accent);
background: var(--accent-soft);
display: flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
position: relative;
}
.as-node-box svg { width: clamp(13px, 3.6cqw, 26px); height: clamp(13px, 3.6cqw, 26px); stroke: var(--accent); }
.as-node-box::before {
content: "";
position: absolute;
inset: -5px;
border-radius: 13px;
border: 1.5px solid var(--accent);
opacity: 0;
}
.as-lane.l1 .as-node-box::before { animation: as-ring 3.2s ease-out infinite; }
.as-lane.l2 .as-node-box::before { animation: as-ring 3.2s ease-out infinite; animation-delay: 0.5s; }
.as-lane.l3 .as-node-box::before { animation: as-ring 3.2s ease-out infinite; animation-delay: 1s; }
@keyframes as-ring {
0% { opacity: 0.5; transform: scale(0.92); }
70% { opacity: 0; transform: scale(1.18); }
100% { opacity: 0; transform: scale(1.18); }
}
.as-node-label {
font-size: clamp(0.44rem, 1.7cqw, 0.85rem);
font-weight: 600;
white-space: nowrap;
}
/* track: progress fills independently per lane, overlapping in time */
.as-track {
position: relative;
flex: 1 1 auto;
min-width: 0;
height: clamp(7px, 2.2cqw, 14px);
border-radius: 999px;
background: var(--paper-alt);
border: 1px solid var(--line);
overflow: hidden;
}
.as-fill {
position: absolute; left: 0; top: 0; bottom: 0;
width: 0%;
background: var(--accent);
border-radius: 999px;
}
.as-lane.l1 .as-fill { animation: as-fill-1 3.2s ease-in-out infinite; }
.as-lane.l2 .as-fill { animation: as-fill-2 3.2s ease-in-out infinite; }
.as-lane.l3 .as-fill { animation: as-fill-3 3.2s ease-in-out infinite; }
@keyframes as-fill-1 {
0% { width: 0%; }
55% { width: 100%; }
70% { width: 100%; }
100% { width: 0%; }
}
@keyframes as-fill-2 {
0%, 15% { width: 0%; }
75% { width: 100%; }
88% { width: 100%; }
100% { width: 0%; }
}
@keyframes as-fill-3 {
0%, 30% { width: 0%; }
90% { width: 100%; }
97% { width: 100%; }
100% { width: 0%; }
}
@container asynccard (max-width: 380px) {
.as-node-label { display: none; }
.as-stage { padding: 0.6rem; gap: 0.7rem; }
}
@media (prefers-reduced-motion: reduce) {
.infographic-asyncexec *, .infographic-asyncexec *::before, .infographic-asyncexec *::after {
animation-duration: 0.001ms !important;
}
}
</style>
<div class="as-stage">
<div class="as-lane l1">
<div class="as-node">
<div class="as-node-box">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m18 16 4-4-4-4"/><path d="m6 8-4 4 4 4"/><path d="m14.5 4-5 16"/></svg>
</div>
<div class="as-node-label">Task A</div>
</div>
<div class="as-track"><div class="as-fill"></div></div>
</div>
<div class="as-lane l2">
<div class="as-node">
<div class="as-node-box">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m18 16 4-4-4-4"/><path d="m6 8-4 4 4 4"/><path d="m14.5 4-5 16"/></svg>
</div>
<div class="as-node-label">Task B</div>
</div>
<div class="as-track"><div class="as-fill"></div></div>
</div>
<div class="as-lane l3">
<div class="as-node">
<div class="as-node-box">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m18 16 4-4-4-4"/><path d="m6 8-4 4 4 4"/><path d="m14.5 4-5 16"/></svg>
</div>
<div class="as-node-label">Task C</div>
</div>
<div class="as-track"><div class="as-fill"></div></div>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-asyncexec')
|| document.querySelector('.infographic-asyncexec');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
})();
</script>📖 What It Means
Slow or heavy tasks get offloaded to run in the background.
Rather than waiting around, the rest of the code continues executing right away.
Once the background task wraps up, a pre-registered callback function fires to handle the result.
💻 Example
console.log('Will read file!');
fs.readFile('./txt/input.txt','utf8',(err, data)=>{
console.log(data);
});
console.log('Reading file...');
readFilekicks off reading the file in the background.The program doesn't pause — it moves straight to the next line.
That's why
"Reading file..."logs before the file's contents — reading takes time.Once the file finishes loading, the callback runs and logs the actual data.
✅ This is what makes it non-blocking — nothing else in the program freezes while the read happens.
🧵 Why Does Node.js Need This? The Single-Thread Explanation
<!-- Infographic: Node.js Runs on a Single Thread — concept, blocking problem, async fix, I/O definition -->
<div class="infographic-nodethread">
<style>
@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,700&family=Inter:wght@400;500;600;700&display=swap');
.infographic-nodethread {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--accent-deep:#1741a6;
--line: #ddd6c9;
font-family: 'Inter', system-ui, sans-serif;
color: var(--ink);
background: var(--paper);
max-width: 880px;
margin: 0 auto;
box-sizing: border-box;
}
.infographic-nodethread[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--accent-deep:#8fb3f5;
--line: #2c313d;
}
.infographic-nodethread * { box-sizing: border-box; }
/* ============================================================= HERO */
.nt-hero {
text-align: center;
padding: clamp(1.75rem, 5vw, 2.75rem) clamp(1.25rem, 5vw, 3rem) 1.5rem;
}
.nt-eyebrow {
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.09em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: 0.6rem;
}
.nt-title {
font-family: 'Fraunces', Georgia, serif;
font-weight: 700;
font-size: clamp(1.4rem, 4vw, 2.1rem);
line-height: 1.15;
margin-bottom: 0.5rem;
}
.nt-title .nt-accentword { color: var(--accent); }
.nt-def {
font-size: 0.9rem;
color: var(--ink-soft);
max-width: 480px;
margin: 0 auto 1.75rem;
line-height: 1.5;
}
/* single thread visual: many user dots funnel into one thread icon */
.nt-thread-visual {
display: flex;
align-items: center;
justify-content: center;
gap: clamp(0.6rem, 3vw, 1.5rem);
flex-wrap: wrap;
}
.nt-users {
display: flex;
gap: 0.4rem;
flex-wrap: wrap;
justify-content: center;
max-width: 220px;
}
.nt-user-dot {
width: clamp(20px, 3vw, 26px);
height: clamp(20px, 3vw, 26px);
border-radius: 50%;
background: var(--paper-alt);
border: 1.5px solid var(--line);
display: flex; align-items: center; justify-content: center;
animation: nt-user-pulse 2.6s ease-in-out infinite;
}
.nt-user-dot svg { width: 55%; height: 55%; stroke: var(--ink-soft); }
.nt-user-dot:nth-child(1) { animation-delay: 0s; }
.nt-user-dot:nth-child(2) { animation-delay: 0.2s; }
.nt-user-dot:nth-child(3) { animation-delay: 0.4s; }
.nt-user-dot:nth-child(4) { animation-delay: 0.6s; }
.nt-user-dot:nth-child(5) { animation-delay: 0.8s; }
.nt-user-dot:nth-child(6) { animation-delay: 1s; }
@keyframes nt-user-pulse { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }
.nt-funnel-line {
position: relative;
width: clamp(40px, 8vw, 80px);
height: 2px;
background: var(--line);
}
.nt-funnel-line .nt-fdot {
position: absolute; top: 50%; left: 0;
width: 6px; height: 6px; border-radius: 50%;
background: var(--accent);
transform: translateY(-50%);
animation: nt-fflow 1.6s linear infinite;
}
.nt-funnel-line .nt-fdot:nth-child(2) { animation-delay: 0.5s; }
.nt-funnel-line .nt-fdot:nth-child(3) { animation-delay: 1s; }
@keyframes nt-fflow {
0% { left: 0%; opacity: 0; }
10% { opacity: 1; }
90% { opacity: 1; }
100% { left: calc(100% - 6px); opacity: 0; }
}
.nt-thread-node { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; }
.nt-thread-ring {
width: clamp(64px, 9vw, 84px);
height: clamp(64px, 9vw, 84px);
border-radius: 50%;
border: 2.5px solid var(--accent);
background: var(--accent-soft);
display: flex; align-items: center; justify-content: center;
position: relative;
}
.nt-thread-ring svg { width: 40%; height: 40%; stroke: var(--accent); }
.nt-thread-ring::before {
content: "";
position: absolute; inset: -6px;
border-radius: 50%;
border: 1.5px solid var(--accent);
opacity: 0;
animation: nt-thread-ring-pulse 2s ease-out infinite;
}
@keyframes nt-thread-ring-pulse {
0% { opacity: 0.5; transform: scale(0.92); }
70% { opacity: 0; transform: scale(1.2); }
100% { opacity: 0; transform: scale(1.2); }
}
.nt-thread-label { font-size: 0.78rem; font-weight: 700; }
.nt-thread-sub { font-size: 0.68rem; color: var(--ink-soft); }
/* ============================================================= COMPARISON */
.nt-compare-wrap { padding: 0 clamp(1.25rem, 5vw, 3rem) 0.5rem; }
.nt-section-title {
font-family: 'Fraunces', Georgia, serif;
font-weight: 700;
font-size: clamp(1.05rem, 2.6vw, 1.3rem);
text-align: center;
margin-bottom: 1.5rem;
}
.nt-compare {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1px;
background: var(--line);
border: 1px solid var(--line);
}
.nt-col {
background: var(--paper);
padding: clamp(1rem, 3vw, 1.5rem);
cursor: pointer;
border-top: 3px solid transparent;
transition: background 0.15s, border-color 0.15s, opacity 0.15s;
}
.nt-col.is-highlighted { background: var(--accent-soft); border-top-color: var(--accent); }
.nt-compare.has-active .nt-col:not(.is-highlighted) { opacity: 0.5; }
.nt-col-head { display: flex; align-items: center; gap: 0.55rem; margin-bottom: 0.5rem; }
.nt-col-icon {
width: 30px; height: 30px; border-radius: 8px;
background: var(--paper-alt); border: 1px solid var(--line);
display: flex; align-items: center; justify-content: center; flex: 0 0 auto;
}
.nt-col-icon svg { width: 16px; height: 16px; stroke: var(--ink-soft); }
.nt-col.is-highlighted .nt-col-icon { background: var(--accent); border-color: var(--accent); }
.nt-col.is-highlighted .nt-col-icon svg { stroke: #fff; }
.nt-col-title { font-weight: 700; font-size: 0.92rem; }
.nt-col-caption { font-size: 0.76rem; color: var(--ink-soft); line-height: 1.4; margin-bottom: 0.9rem; min-height: 2.2em; }
.nt-mini-lane { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem; }
.nt-mini-icon {
width: 20px; height: 20px; border-radius: 6px;
border: 1.5px dashed var(--line);
display: flex; align-items: center; justify-content: center; flex: 0 0 auto;
transition: border-color 0.2s, background 0.2s;
}
.nt-mini-icon svg { width: 11px; height: 11px; stroke: var(--ink-soft); transition: stroke 0.2s; }
.nt-mini-track {
position: relative; flex: 1 1 auto; height: 6px; border-radius: 999px;
background: var(--paper-alt); border: 1px solid var(--line); overflow: hidden;
}
.nt-mini-fill { position: absolute; left: 0; top: 0; bottom: 0; width: 0%; background: var(--accent); border-radius: 999px; }
.nt-mini-check {
position: absolute; right: 2px; top: 50%; transform: translateY(-50%);
font-size: 7px; font-weight: 900; color: #fff; opacity: 0;
}
/* blocking: strictly sequential, second lane visibly waits */
.nt-block .nt-mini-lane.b1 .nt-mini-icon { animation: nt-b-icon-1 4s ease-in-out infinite; }
.nt-block .nt-mini-lane.b1 .nt-mini-icon svg { animation: nt-b-svg-1 4s ease-in-out infinite; }
.nt-block .nt-mini-lane.b1 .nt-mini-fill { animation: nt-b-fill-1 4s ease-in-out infinite; }
.nt-block .nt-mini-lane.b2 .nt-mini-icon { animation: nt-b-icon-2 4s ease-in-out infinite; }
.nt-block .nt-mini-lane.b2 .nt-mini-icon svg { animation: nt-b-svg-2 4s ease-in-out infinite; }
.nt-block .nt-mini-lane.b2 .nt-mini-fill { animation: nt-b-fill-2 4s ease-in-out infinite; }
@keyframes nt-b-icon-1 { 0%,2%{border-color:var(--line);background:transparent;} 6%,44%{border-color:var(--accent);background:var(--accent-soft);} 48%,100%{border-color:var(--line);background:transparent;} }
@keyframes nt-b-svg-1 { 0%,2%{stroke:var(--ink-soft);} 6%,44%{stroke:var(--accent);} 48%,100%{stroke:var(--ink-soft);} }
@keyframes nt-b-fill-1 { 0%,2%{width:0%;} 44%{width:100%;} 48%,100%{width:0%;} }
@keyframes nt-b-icon-2 { 0%,50%{border-color:var(--line);background:transparent;} 54%,92%{border-color:var(--accent);background:var(--accent-soft);} 96%,100%{border-color:var(--line);background:transparent;} }
@keyframes nt-b-svg-2 { 0%,50%{stroke:var(--ink-soft);} 54%,92%{stroke:var(--accent);} 96%,100%{stroke:var(--ink-soft);} }
@keyframes nt-b-fill-2 { 0%,50%{width:0%;} 92%{width:100%;} 96%,100%{width:0%;} }
/* async: overlapping, both run at once, callback check fires on completion */
.nt-async .nt-mini-lane.a1 .nt-mini-icon { border-style: dashed; border-color: var(--accent); background: var(--accent-soft); }
.nt-async .nt-mini-lane.a1 .nt-mini-icon svg { stroke: var(--accent); }
.nt-async .nt-mini-lane.a2 .nt-mini-icon { border-style: dashed; border-color: var(--accent); background: var(--accent-soft); }
.nt-async .nt-mini-lane.a2 .nt-mini-icon svg { stroke: var(--accent); }
.nt-async .nt-mini-lane.a1 .nt-mini-fill { animation: nt-a-fill-1 4s ease-in-out infinite; }
.nt-async .nt-mini-lane.a2 .nt-mini-fill { animation: nt-a-fill-2 4s ease-in-out infinite; }
.nt-async .nt-mini-lane.a1 .nt-mini-check { animation: nt-a-check-1 4s ease-in-out infinite; }
.nt-async .nt-mini-lane.a2 .nt-mini-check { animation: nt-a-check-2 4s ease-in-out infinite; }
@keyframes nt-a-fill-1 { 0%{width:0%;} 46%{width:100%;} 58%{width:100%;} 100%{width:0%;} }
@keyframes nt-a-fill-2 { 0%,12%{width:0%;} 62%{width:100%;} 74%{width:100%;} 100%{width:0%;} }
@keyframes nt-a-check-1 { 0%,42%{opacity:0;} 48%,54%{opacity:1;} 60%{opacity:0;} 100%{opacity:0;} }
@keyframes nt-a-check-2 { 0%,58%{opacity:0;} 64%,70%{opacity:1;} 76%{opacity:0;} 100%{opacity:0;} }
@media (max-width: 560px) {
.nt-compare { grid-template-columns: 1fr; }
}
/* ============================================================= NOTE */
.nt-note {
text-align: center;
font-size: 0.74rem;
font-style: italic;
color: var(--ink-soft);
padding: 1.25rem clamp(1.25rem, 5vw, 3rem) 0.25rem;
}
/* ============================================================= I/O FOOTER */
.nt-io {
display: flex;
align-items: center;
justify-content: center;
gap: clamp(1rem, 4vw, 2rem);
flex-wrap: wrap;
padding: 1.75rem clamp(1.25rem, 5vw, 3rem) 2.25rem;
margin-top: 0.75rem;
}
.nt-io-label {
font-size: 0.82rem;
font-weight: 700;
color: var(--ink-soft);
white-space: nowrap;
}
.nt-io-chip {
display: flex; align-items: center; gap: 0.4rem;
padding: 0.4rem 0.75rem;
border: 1px solid var(--line);
border-radius: 999px;
background: var(--paper-alt);
font-size: 0.78rem;
font-weight: 600;
white-space: nowrap;
}
.nt-io-chip svg { width: 15px; height: 15px; stroke: var(--accent); }
@media (prefers-reduced-motion: reduce) {
.infographic-nodethread *, .infographic-nodethread *::before, .infographic-nodethread *::after {
animation-duration: 0.001ms !important;
}
}
</style>
<!-- HERO: single thread concept -->
<div class="nt-hero">
<div class="nt-thread-visual">
<div class="nt-users">
<div class="nt-user-dot"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg></div>
<div class="nt-user-dot"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg></div>
<div class="nt-user-dot"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg></div>
<div class="nt-user-dot"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg></div>
<div class="nt-user-dot"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg></div>
<div class="nt-user-dot"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg></div>
</div>
<div class="nt-funnel-line"><div class="nt-fdot"></div><div class="nt-fdot"></div><div class="nt-fdot"></div></div>
<div class="nt-thread-node">
<div class="nt-thread-ring"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2"/><rect x="9" y="9" width="6" height="6"/><path d="M15 2v2"/><path d="M15 20v2"/><path d="M2 15h2"/><path d="M2 9h2"/><path d="M20 15h2"/><path d="M20 9h2"/><path d="M9 2v2"/><path d="M9 20v2"/></svg></div>
<div class="nt-thread-label">1 Thread</div>
<div class="nt-thread-sub">shared by everyone</div>
</div>
</div>
</div>
<!-- COMPARISON: blocking vs async -->
<div class="nt-compare-wrap">
<div class="nt-compare" id="ntCompare">
<div class="nt-col nt-block" data-col="block">
<div class="nt-col-head">
<div class="nt-col-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 22h14"/><path d="M5 2h14"/><path d="M17 22v-4.172a2 2 0 0 0-.586-1.414L12 12l-4.414 4.414A2 2 0 0 0 7 17.828V22"/><path d="M7 2v4.172a2 2 0 0 0 .586 1.414L12 12l4.414-4.414A2 2 0 0 0 17 6.172V2"/></svg></div>
<div class="nt-col-title">Blocking</div>
</div>
<div class="nt-col-caption">One slow task holds up everyone waiting behind it in line.</div>
<div class="nt-mini-lane b1">
<div class="nt-mini-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg></div>
<div class="nt-mini-track"><div class="nt-mini-fill"></div></div>
</div>
<div class="nt-mini-lane b2">
<div class="nt-mini-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg></div>
<div class="nt-mini-track"><div class="nt-mini-fill"></div></div>
</div>
</div>
<div class="nt-col nt-async" data-col="async">
<div class="nt-col-head">
<div class="nt-col-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg></div>
<div class="nt-col-title">Non-Blocking</div>
</div>
<div class="nt-col-caption">Main thread stays free; each callback runs when its task is ready.</div>
<div class="nt-mini-lane a1">
<div class="nt-mini-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg></div>
<div class="nt-mini-track"><div class="nt-mini-fill"></div><div class="nt-mini-check">✓</div></div>
</div>
<div class="nt-mini-lane a2">
<div class="nt-mini-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg></div>
<div class="nt-mini-track"><div class="nt-mini-fill"></div><div class="nt-mini-check">✓</div></div>
</div>
</div>
</div>
</div>
<!-- I/O FOOTER -->
<div class="nt-io">
<div class="nt-io-label">I/O = Input / Output</div>
<div class="nt-io-chip">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/></svg>
Files
</div>
<div class="nt-io-chip">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/></svg>
Network
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-nodethread')
|| document.querySelector('.infographic-nodethread');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
const compare = wrapper.querySelector('#ntCompare');
compare.querySelectorAll('.nt-col').forEach(function (col) {
col.addEventListener('click', function () {
const already = col.classList.contains('is-highlighted');
compare.querySelectorAll('.nt-col').forEach(function (c) { c.classList.remove('is-highlighted'); });
if (!already) {
col.classList.add('is-highlighted');
compare.classList.add('has-active');
} else {
compare.classList.remove('has-active');
}
});
});
})();
</script>🔑 Node.js Runs on a Single Thread
A thread is the path of instructions the CPU runs — it's where your code executes.
A Node.js app runs on just one thread, no matter how many users it has — 5 or 5 million, everyone shares it.
⚠️ Blocking Code on a Single Thread
If one user triggers blocking code — say, a file read that takes a second — everyone else has to wait that same second before the thread is free again.
Even simple actions like logging in get stuck in line.
With a few users, you won't notice. At scale, it becomes a real bottleneck.
Note: This is simplified — we'll cover Node's internals more deeply later.
Async file reading moves the heavy work off the main thread.
The main thread stays free to handle other requests.
Once the background task finishes, its callback runs to process the result.
End result: every user gets served efficiently, with no single slow task holding up the rest.
I/O = Input/Output — operations like:
📂 Reading/writing files
🌐 Handling network requests
Node.js's entire design (built around callbacks) exists specifically to support non-blocking I/O, making it fast and scalable.
Languages like PHP use a different model — a new thread is created for each user.
Node.js's creator chose the single-thread + non-blocking callback model instead, believing it's better suited for building highly performant, scalable web applications.
💡 Important Clarification About Callbacks
Passing a function into another function (a "callback") is common in JavaScript — but this does NOT automatically make code asynchronous.
Only certain built-in Node API functions (like
readFile) are specifically designed to behave asynchronously.Many more such functions will be explored throughout the course.