۶ تصور اشتباه رایج درباره Event Loop در Node.js
Node.js به خاطر I/O غیرمسدودکننده و معماری event-driven معروفه. ولی event loop هنوز یکی از ناشناختهترین بخشهاشه. حتی بچههای با تجربه هم گاهی گیج میشن و این باعث تصورات اشتباهی میشه که روی کدنویسی و بهینهسازیشون تأثیر میذاره.
اگه تا حالا شنیدی که "Node.js تکنخیه، پس نمیتونه کارهای همزمان رو مدیریت کنه" یا "setTimeout اجرا رو توی event loop به تأخیر میندازه"، بدان که تنها نیستی.
۱. تصور اشتباه: Node.js تکنخیه، پس نمیتونه عملیات همزمان رو مدیریت کنه
واقعیت:
Node.js یه runtime JavaScript تکنخی داره، ولی توی مدیریت taskهای ناهمزمان تکنخی نیست.
وقتی میشنون که Node.js تکنخیه، فکر میکنن نمیتونه چند تا عملیات رو همزمان انجام بده. ولی این درست نیست. event loop و libuv (همون کتابخانهای که I/O ناهمزمان رو توی Node.js قدرت میده) بهش اجازه میدن taskهای غیرمسدودکننده رو با استفاده از threadهای پسزمینه از یه worker pool انجام بده.
مثلاً وقتی یه درخواست I/O میدی (مثل خوندن یه فایل یا query کردن دیتابیس)، Node.js task رو به یه thread جداگانه توی پسزمینه میسپره. بعد از تموم شدن، callback به event loop push میشه تا اجرا بشه.
چی باید بدونی:
Node.js از چند تا thread توی زیرساخت برای عملیات I/O، taskهای سیستم فایل و حتی taskهای CPU-intensive با Worker Threads API استفاده میکنه.
طبیعت تکنخی فقط برای اجرای کد JavaScript توی thread اصلی اعمال میشه.
event loop به صورت کارآمد چند تا عملیات ناهمزمان رو مدیریت میکنه، که Node.js رو برای مدیریت اتصالات همزمان عالی میکنه.
مثال:
const fs = require('fs');
console.log("Start reading file...");
fs.readFile("example.txt", "utf8", (err, data) => {
if (err) throw err;
console.log("File content:", data);
});
console.log("Other tasks can run while file is being read!");حتی اگه عملیات readFile ناهمزمان باشه، Node.js اجرای taskهای دیگه رو در حالی که منتظر خوندن فایله قفل نمیکنه.
۲. تصور اشتباه: setTimeout اجرا رو بعد از تأخیر مشخص شده تضمین میکنه
واقعیت:
setTimeout اجرای دقیق بعد از تأخیر داده شده رو تضمین نمیکنه. به جاش، یه callback رو بعد از حداقل زمان مشخص شده schedule میکنه.
چی باید بدونی:
setTimeout callback رو توی صف Timers قرار میده ولی اجراش نمیکنه تا وقتی که event loop در دسترس باشه.
اگه call stack مشغول باشه، اجرا ممکنه بیشتر از timeout مشخص شده به تأخیر بیفته.
تأخیر واقعی بستگی داره که چند تا task توی event loop قبل از اون قرار دارن.
مثال:
console.log("Start");
setTimeout(() => {
console.log("Executed after 2 seconds");
}, 2000);
const start = Date.now();
while (Date.now() - start < 3000) {
// Blocking the event loop for 3 seconds
}
console.log("End");خروجی مورد انتظار:
Start
End
Executed after 2 seconds (actually after ~3s due to blocking)چون event loop برای ۳ ثانیه قفل شده، callback setTimeout به تأخیر میافته. این ثابت میکنه که setTimeout(2000) اجرای دقیق بعد از ۲ ثانیه رو تضمین نمیکنه.
۳. تصور اشتباه: Promises و async/await از event loop عبور میکنن
واقعیت:
Promises و async/await از event loop عبور نمیکنن. به جاش، callbackها رو توی صف microtask schedule میکنن که اولویت بالاتری نسبت به صف callback معمولی داره.
چی باید بدونی:
Microtaskها (مثل process.nextTick() و Promise.then()) قبل از callbackهای I/O، timerها و taskهای schedule شده دیگه اجرا میشن.
async/await فقط syntactic sugar روی Promises هست و عملیات ناهمزمان رو synchronous نمیکنه.
حتی اگه await کد رو blocking به نظر برسونه، باز هم کنترل رو به event loop میده که به عملیات دیگه امکان اجرا میده.
مثال:
console.log("Start");
setTimeout(() => console.log("setTimeout callback"), 0);
Promise.resolve().then(() => console.log("Promise callback"));
console.log("End");خروجی مورد انتظار:
Start
End
Promise callback
setTimeout callbackحتی اگه setTimeout با تأخیر 0ms schedule شده باشه، callback Promise اول اجرا میشه چون microtaskها (Promises) قبل از تکرار بعدی event loop اجرا میشن.
۴. تصور اشتباه: process.nextTick و setImmediate یکسانن
واقعیت:
process.nextTick() و setImmediate() توی نحوه schedule کردن callbackها متفاوتن:
process.nextTick() یه callback رو برای اجرای فوری بعد از عملیات فعلی ولی قبل از هر task I/O یا timer schedule میکنه.
setImmediate() یه callback رو بعد از taskهای I/O ولی قبل از timerها schedule میکنه.
چی باید بدونی:
استفاده بیش از حد از process.nextTick() میتونه event loop رو با اجرای مداوم microtaskها گرسنه کنه که از اجرای عملیات I/O جلوگیری میکنه.
setImmediate() معمولاً انتخاب بهتری هست وقتی میخوای یه callback رو بعد از I/O ولی قبل از تکرار بعدی event loop اجرا کنی.
مثال:
const fs = require('fs');
fs.readFile(__filename, () => {
setImmediate(() => console.log("setImmediate callback"));
process.nextTick(() => console.log("process.nextTick callback"));
});
console.log("Start");خروجی مورد انتظار:
Start
process.nextTick callback
setImmediate callbackحتی اگه setImmediate() اول schedule شده باشه، process.nextTick() اول اجرا میشه چون قبل از taskهای I/O توی event loop اجرا میشه.
۵. تصور اشتباه: event loop فقط وقتی اجرا میشه که یه task در انتظار باشه
واقعیت:
event loop هیچ وقت از اجرا متوقف نمیشه تا وقتی که فرآیند فعاله. به طور مداوم برای timerهای در انتظار، رویدادهای I/O و callbackهای schedule شده بررسی میکنه.
چی باید بدونی:
event loop وقتی taskی وجود نداره متوقف نمیشه — به polling برای کار جدید ادامه میده.
اگه taskی باقی نمونده باشه، فرآیند Node.js به طور خودکار خارج میشه.
نگه داشتن یه event listener باز، setInterval() یا یه Promise حلنشده میتونه event loop رو به طور نامحدود در حال اجرا نگه داره.
۶. تصور اشتباه: کارهای CPU سنگین توی event loop Node.js خوب کار میکنن
واقعیت:
Node.js برای taskهای CPU-intensive ایدهآل نیست چون یه عملیات طولانیمدت event loop رو قفل میکنه که از اجرای taskهای دیگه جلوگیری میکنه.
چی باید بدونی:
برای عملیات CPU-heavy، از Worker Threads برای اجرای taskها به صورت موازی استفاده کن.
از قفل کردن event loop با محاسبات بزرگ دوری کن — اونا رو به child processes یا یه سرویس خارجی منتقل کن.
مثال مسدود کردن Event Loop:
console.log("Start");
setTimeout(() => console.log("Timeout executed"), 0);
for (let i = 0; i < 1e9; i++) {} // Simulating heavy computation
console.log("End");اینجا، event loop قفل میشه که اجرای callback setTimeout رو به تأخیر میندازه.
راهحل: منتقل کردن کار CPU
const { Worker } = require('worker_threads');
const worker = new Worker('./heavyTask.js');
worker.on('message', message => console.log(message));با استفاده از Worker Threads، از قفل کردن thread اصلی دوری میکنی که event loop رو responsive نگه میداره.
نتیجهگیری
درک event loop توی Node.js برای نوشتن اپلیکیشنهای کارآمد و مقیاسپذیر خیلی مهمه. با درک درست از نحوه کار event loop، میتونی کد بهتری بنویسی و از مشکلات رایج عملکردی دوری کنی.
یادت باشه:
Node.js تکنخیه ولی میتونه عملیات همزمان رو مدیریت کنه
setTimeout تضمین نمیکنه که callback دقیقاً توی زمان مشخص شده اجرا بشه
Promises و async/await از event loop عبور نمیکنن
process.nextTick و setImmediate متفاوتن
event loop به طور مداوم اجرا میشه تا وقتی که فرآیند فعاله
برای کارهای CPU-intensive از Worker Threads استفاده کن





