- Knowledge
- technology
- OOP
- Tips
- Programming
- Tips
- Tutorial
- SEO
- Ranking
- Knowledge
- Special Day
- Seo
- Bug
- Data science
- Seo
- artificial intelligence
- Machine Learning
- Robotics
- happyNewYear2021
- newYearEve
- 2021
- Automation
- Smart Home
- Career
- Best Practices
- Git
- Logging
- Web Fundamentals
- DNS
- HTTPS
- Performance
- AI Tools
- ChatGPT
- Claude
- Gemini
- Laravel
- Eloquent
- MySQL
- HTTPS
- TLS
- Web Security
- Certificates
- Developer Life
- Debugging
- Docker
- DevOps
- Transactions
- Queues
- LLMs
- AI
- AI Coding
- Developer Tools
- React Native
- Expo
- Kate PMS
- Mobile Apps
- Laravel
- Authentication
- Sanctum
- Cookies
- API Design
- Payments
- Idempotency
- DeepSeek
- Open Source AI
- LLMs
- AI News
- Git
- Version Control
- AI Coding
- Prompting
- PHP
- Checklist
- MCP
- AI Agents
- OpenAI
- Architecture
- Microservices
- Modular Monolith
- Estimation
- Developer Life
- Project Planning
- Humour
- OAuth
- OpenID Connect
- Authentication
- Embeddings
- Vector Search
- RAG
- pgvector
- OpenAI
- GPT-4.1
- Codex CLI
- Events
- Testing
- Clean Code
- Maintainability
- Code Review
- Webhooks
- API
- Security
- Claude Code
- Workflow
- AI
- LLM
- Prompt Injection
- Mobile
- React
- Networking
- TCP
- UDP
- HTTP/3
- CLAUDE.md
- AWS
- Cloud Security
- Backups
- PHPUnit
- Software Engineering
- Leadership
- Communication
- RAG
- Embeddings
- AI Engineering
- IT Infrastructure
- Networking
- Access Control
- CI/CD
- GitHub Actions
- Gemini CLI
- Claude Code
- JavaScript
- Async/Await
- Node.js
- Promises
- Security
- Cryptography
- Passwords
- MySQL
- Database
- Vibe Coding
- Software Quality
- DNS
- Code Reading
- Onboarding
- Productivity
- Background Jobs
- Developer Humour
- Estimates
- Dev Life
- JWT
- o3-mini
- DeepSeek R1
- Rate Limiting
- Kate PMS
- E-Signing
- Audit Trail
- REST
- GraphQL
- API Design
- Laravel 12
- Upgrade Guide
- Open Source
- Self-Hosting
- Task Scheduling
- Cron
- Secrets
- CORS
- PHP
- PHP-FPM
- OPcache
- GitHub Copilot
- Software Architecture
- Engineering
- TypeScript
- JavaScript
- Type Safety
- AI Security
- React Native
- Product Design
- AI Agents
- Kiro
- Queues
- Redis
- RabbitMQ
- AWS SQS
- Nginx
- Apache
- GPT-5
- gpt-oss
- Clean Code
- Architecture
- Naming
- Documentation
- Career
- ADR
- Teamwork
- Supply Chain
- Kate HRM
- HR Software
- Permissions
- System Design
- Pagination
- SSH
- Linux
- Big O
- Databases
- Laravel Boost
- MCP
- Developer Skills
- Validation
- Databases
- Indexes
- Code Quality
- Deployment
- Developer Humour
- Feature Flags
- Code Review
- Pull Requests
- Docker
- Cursor
- Authorization
- RBAC
- Gemini
- Long Context
- PHP 8.4
- Caching
- Dependency Injection
- Web Performance
- Browser
- CSS
- Database
- Migrations
- ChatGPT
- AI for Developers
- Monitoring
- On-Call
- REST
- Backend
- SQL
- NoSQL
- Database Design
- Coding Agents
- Claude 4
- API Resources
- REST API
- Load Balancing
- Scaling
- AWS
- AI Tools
- Claude
- Sora 2
- CTE
- 2FA
- TOTP
- Programming Languages
- Prompts
- Developer Workflow
- API Gateway
- APIs
- Passport
- API Auth
- Learning
- Burnout
- Developer Growth
- Web Development
- SEO
- Kate Mall
- ChatGPT Atlas
- Agent Skills
- Middleware
- Laravel 12
- Collections
- Context Window
- Monitoring
- Commit Messages
- Self Review
- Growth
- Regex
- Programming Basics
- Text Processing
- Database Design
- Normalization
- Linux
- Server Security
- Linux Foundation
- Open Standards
- Legacy Code
- Documentation
- AI Workflow
- File Uploads
- Test Data
- Hashing
- Performance
- Caching
- Enums
- Scope Creep
- Estimation
- Codex
- Gemini CLI
- Timezones
- Carbon
- Bugs
- PHP 8.5
- Gemini 3
- GPT-5.1
- Data Integrity
- Event Loop
- Async
- Opus 4.5
- AI Models
- React
- Forms
- Frontend
- Backups
- AI Images
- DALL-E
- Midjourney
- Race Conditions
- Concurrency
- Legacy Code
- Refactoring
- Senior Engineer
- Scope
- LLM
- CDN
- Web
- Sub-Agents
- Soft Deletes
- Audit Log
- Concurrency
- AI Learning
- NestJS
- AI Evals
- Policies
- SPF DKIM DMARC
- Unicode
- UTF-8
- Knowledge Graph
- Value Objects
- Technical Debt
- Feature Flags
- Laravel Pennant
- Deployment
- Copilot
- Composer
- Dependencies
- Artisan
- Automation
- AWS S3
- Object Storage
- Cloud
- Small Language Models
- Ollama
- Production
- Sessions
- HTTP
- Mentoring
- SQL
- Virtual Machines
- Web Development
- HTTP/2
- QUIC
- Web Performance
- AI Integration
- LLM API
- SOLID
- OOP
- Hosting
- Serverless
- Merge Conflicts
- Temperature
- AI Development
- Reverse Proxy
- Nginx
- Infrastructure
- Verification
- Passkeys
- WebAuthn
- Teams
- Communication
- Stakeholders
- Monorepo
- CI/CD
- Versioning
- JSON Schema
- Livewire
- Inertia
- Meetings
- Distributed Systems
- Privacy
- Full-Stack
- T-Shaped Skills
- Money
- Notifications
- Web Security
- HTTP Headers
- CSP
- Function Calling
- Load Testing
- k6
- Data Extraction
- Debugging
- WebSockets
- SSE
- Real-Time
- Laravel Reverb
- Infrastructure as Code
- Terraform
- Side Projects
- Laravel Pint
- OpenAPI
- Swagger
- UX
- Multimodal
- Jest
- Pair Programming
- APIs
- Rate Limiting
- Resilience
- Dev Humour
- Design Tokens
- JWT
- API Keys
- Sessions
- PHPStan
- Rector
- Incidents
- Reporting
- Dashboards
- Zero Trust
- IAM
- Search
- Laravel Scout
- Junior Developers
- Mentoring
- Images
- WebP
- AVIF
- Bug Reports
- Let's Encrypt
- Design Docs
- Software Design
- Observers
- Replication
- Accountability
- Data Structures
- Reliability
- LLM Memory
- Error Handling
- Payments
- Payment Gateway
- Webhooks
- PCI DSS
- Observability
- OpenTelemetry
- Personal Brand
- Writing
- Conventions
- Dates
- Scheduling
- Disaster Recovery
- Compression
- Brotli
- Deadlines
- Developer Habits
- State Machines
- Tech Roles
- UUID
- ULID
- Horizon
- Planning
- Engineering Culture
- Ownership
- Soft Skills
- Socialite
- Cost Control
- Collations
- Unicode
- Octane
- PostgreSQL
The JavaScript Event Loop Explained: Why setTimeout(0) Isn't Instant
About Post
Quick quiz before we start. What does this print?
console.log('A');
setTimeout(() => console.log('B'), 0);
Promise.resolve().then(() => console.log('C'));
queueMicrotask(() => console.log('D'));
console.log('E');
If you said A, E, C, D, B, you can probably skim this. If you expected B to come earlier, because the timeout is literally zero, you're in very good company. And once you see why it's last, a whole category of "why is this running in the wrong order?" bugs stops being mysterious.
The kitchen with one chef
JavaScript runs your code on a single thread. Picture a kitchen with exactly one chef. The chef can only do one thing at a time, but the kitchen still serves many tables, because the slow parts (the oven, the delivery driver, the fridge) don't need the chef standing next to them.
That's the whole trick. Four pieces make it work:
- The call stack is what the chef is doing right now. Functions are pushed on when called and popped off when they return.
- The environment (the browser's Web APIs, or libuv in Node) is the oven and the timers. It handles waiting: network requests, timers, file reads. Not your JavaScript.
- The task queue (also called the macrotask or callback queue) is the line of order tickets: "the timer finished", "the click happened", "the response arrived".
- The microtask queue is a small stack of urgent notes stuck to the chef's station: promise callbacks,
awaitcontinuations andqueueMicrotask().
The loop itself
The event loop is the chef's routine, repeated forever:
- Take one task from the task queue and run it until the call stack is empty.
- Run every microtask, including any new microtasks added while doing so, until the microtask queue is empty.
- In a browser, render if it's time to update the screen.
- Go back to step 1.
The important asymmetry: one task at a time, but all the microtasks at once. Microtasks always jump the queue ahead of the next task.
Back to the quiz
Now the order makes sense:
- The whole script is itself a task. It logs A.
setTimeouthands a timer to the environment. When it fires, its callback joins the task queue.- The
.then()callback andqueueMicrotaskgo into the microtask queue. - The script logs E and finishes. The call stack is empty.
- Microtasks run, in order: C, then D.
- Only now does the loop pick the next task: the timer callback, B.
await follows the same rule. Everything after an await is a microtask, which is why this prints 1, 3, 2:
async function load() {
console.log('1');
await null;
console.log('2'); // continues in a microtask
}
load();
console.log('3');
Why setTimeout(fn, 0) is not instant
The delay is a minimum, not a promise. "Zero" really means "put this in the task queue as soon as possible". The callback still waits for:
- the current code to finish,
- every pending microtask,
- any tasks already ahead of it in the queue.
On top of that, browsers clamp deeply nested timers (a timeout scheduled from a timeout, several levels deep) to at least 4 ms, and they throttle timers heavily in background tabs. Node treats a delay of 0 as 1 ms. So setTimeout(fn, 0) is a useful way to say "after the current work", never a way to say "now".
The two ways to break the loop
Block the stack. While your code runs, nothing else can: no clicks, no rendering, no other callbacks. In a browser, this freezes the page:
button.addEventListener('click', () => {
const end = Date.now() + 3000;
while (Date.now() < end) {} // three seconds of frozen page
});
In Node, the same mistake freezes every request your server is handling, not just one. Heavy CPU work belongs in a Web Worker, a worker thread or a queue job, or should be split into chunks that yield back to the loop between pieces.
Starve it with microtasks. Because the loop drains all microtasks before moving on, a microtask that keeps scheduling another microtask means the next task, and the next render, never comes. It's rarer, but it's the same freeze wearing a different hat.
The mental model in one line: one task, then all the microtasks, then (in the browser) maybe a render, repeat. Promises and await are microtasks; timers, events and I/O callbacks are tasks.
Node.js: same idea, extra details
Node has the same basic loop, built on libuv, with a few additions you'll meet sooner or later:
- Phases. Node's loop cycles through phases: timers, I/O callbacks, a "check" phase for
setImmediate, close callbacks and a few internal ones. Microtasks run between each callback. process.nextTick()has its own queue that, in a CommonJS script, runs even before promise microtasks. Powerful, and easy to overuse.setImmediate()vssetTimeout(fn, 0). From the main script, their order isn't guaranteed. Inside an I/O callback,setImmediatealways runs first.
setTimeout(() => console.log('timeout'), 0);
setImmediate(() => console.log('immediate'));
process.nextTick(() => console.log('nextTick'));
Promise.resolve().then(() => console.log('promise'));
// CommonJS: nextTick, promise, then timeout/immediate in either order
One more gotcha: in an ES module, top-level code already runs inside the promise machinery, so promise can print before nextTick. Yet another reason not to write code that depends on these fine-grained orderings.
What to take away
- JavaScript runs one thing at a time; the environment does the waiting.
- Microtasks (promises,
await) always run before the next task (timers, events, I/O). setTimeout(fn, 0)means "soon, after everything else", not "now".- Long synchronous work freezes everything. Move it off the main thread or split it up.
- If your code depends on exact ordering between timers, promises and ticks, make the dependency explicit with
awaitinstead.
Which ordering surprised you the first time you hit it: the setTimeout zero, the await continuation, or something stranger?

Be first to comment it...