- Published on
Event Driven: Tokio vs Node.js
- Authors

- Name
- Sidharth Singh
- https://x.com/sid10singh
At the core, Tokio uses the OS (like epoll/kqueue/IOCP) to wait for events such as:
“socket is ready to read” “timer expired” “file descriptor is writable”
Instead of blocking, a task says: “Wake me when this event happens.”
So the flow looks like:
Task tries to read/write → not ready Tokio registers interest with the OS Task yields (doesn’t block the thread) OS signals event → Tokio wakes the task Task resumes exactly where it left off
This is why Rust async uses Future + poll: tasks are repeatedly polled when events occur.
Tokio vs Node.js: same idea, different machinery
- Execution model
Node.js
Single-threaded event loop One main thread handles all JS execution Uses callbacks / promises Heavy reliance on libuv
Tokio
Multi-threaded runtime by default Many tasks (lightweight futures) scheduled across threads Uses Rust’s async/await (zero-cost abstractions)
👉 Big difference: Tokio = event-driven + work-stealing thread pool Node = event-driven + single-thread loop
Node.js: concurrency only (one thread executes JS) Tokio: concurrency and parallelism (multiple threads can run tasks simultaneously)
So if you spawn 1000 tasks:
Node → all handled on one thread Tokio → spread across CPU cores
ow async resumes
Node.js
Event loop pushes callbacks into queues Executes them one by one
Tokio
Futures implement poll() Tasks are woken via wakers Scheduler decides which thread runs them
Tokio is more like:
“resume this suspended computation exactly where it paused”
Node is more like:
“call this callback when ready”
Event-driven ≠ Event-driven architecture
This is where confusion usually happens:
Tokio “event-driven” → low-level runtime mechanism Node “event-driven architecture” → application style (emitters, listeners, streams)
Node popularized patterns like:
EventEmitter pub/sub systems
Tokio doesn’t enforce that. You can build event-driven apps in Rust, but Tokio itself is just the engine underneath.