নতুনদের জন্য Node.js ইভেন্ট লুপ ব্যাখ্যা
আপনি সম্ভবত শুনেছেন যে Node.js একক-থ্রেডেড এবং হাজার হাজার সমকালীন সংযোগ পরিচালনা করতে একটি ইভেন্ট লুপ ব্যবহার করে। কিন্তু এর প্রকৃত অর্থ কী? আপনি যদি Node.js-এ নতুন হন, তাহলে ইভেন্ট লুপ একটি রহস্যময় ব্ল্যাক বক্স বলে মনে হতে পারে। এই নিবন্ধটি সহজ ভাষায় এটি ব্যাখ্যা করে, যাতে আপনি আরও ভালো, দ্রুত Node.js কোড লিখতে পারেন।
ইভেন্ট লুপ কেন বিদ্যমান
প্রচলিত ওয়েব সার্ভার (যেমন PHP সহ Apache) প্রতিটি অনুরোধের জন্য একটি নতুন থ্রেড বা প্রক্রিয়া তৈরি করে। এটি কাজ করে, কিন্তু যখন আপনার অনেক সমকালীন সংযোগ থাকে তখন এটি ভালোভাবে স্কেল করে না—প্রতিটি থ্রেড মেমরি এবং CPU ব্যবহার করে। Node.js একটি ভিন্ন পদ্ধতি গ্রহণ করে: এটি আপনার জাভাস্ক্রিপ্ট একটি একক থ্রেডে চালায় এবং I/O অপারেশনগুলি অ্যাসিঙ্ক্রোনাসভাবে পরিচালনা করতে একটি ইভেন্ট লুপ ব্যবহার করে। এর মানে আপনার কোড ডেটাবেস কোয়েরি বা ফাইল রিড শেষ হওয়ার জন্য অপেক্ষা করে না; এটি অন্যান্য কাজ চালিয়ে যায় এবং ফলাফল প্রস্তুত হলে ফিরে আসে।
মূল উপাদানগুলি
ইভেন্ট লুপে ডুব দেওয়ার আগে, আসুন মূল অংশগুলি স্পষ্ট করি:
- কল স্ট্যাক: যেখানে আপনার সিঙ্ক্রোনাস জাভাস্ক্রিপ্ট কোড চলে। ফাংশনগুলি সম্পাদিত হওয়ার সাথে সাথে পুশ এবং পপ হয়।
- Node API: C++ API যা ফাইল I/O, নেটওয়ার্ক অনুরোধ এবং টাইমারের মতো ব্যয়বহুল অপারেশন পরিচালনা করে। এগুলি ব্যাকগ্রাউন্ডে চলে (libuv থ্রেড পুল ব্যবহার করে)।
- কলব্যাক কিউ: যেখানে সম্পন্ন Node API অপারেশন থেকে কলব্যাকগুলি সম্পাদিত হওয়ার জন্য অপেক্ষা করে।
- ইভেন্ট লুপ: অর্কেস্ট্রেটর যা ক্রমাগত পরীক্ষা করে কল স্ট্যাক খালি কিনা এবং যদি তাই হয়, কিউ থেকে কলব্যাকগুলি স্ট্যাকে স্থানান্তর করে।
ইভেন্ট লুপ কীভাবে কাজ করে: একটি সহজ উদাহরণ
এই কোডটি বিবেচনা করুন:
console.log('Start');
setTimeout(() => {
console.log('Timeout');
}, 0);
console.log('End');
আউটপুট কী? যদি আপনি অনুমান করেন Start, End, Timeout, আপনি সঠিক। যদিও টাইমআউটটি 0 মিলিসেকেন্ড, কলব্যাকটি অবিলম্বে সম্পাদিত হয় না। এখানে যা ঘটে:
console.log('Start')কল স্ট্যাকে পুশ হয়, সম্পাদিত হয় এবং পপ হয়।setTimeoutকল করা হয়। Node.js টাইমারটি নিবন্ধন করে এবং এটি 0ms পরে মেয়াদ শেষ হওয়ার জন্য সেট করে। কলব্যাকটি সংরক্ষণ করা হয় এবং ফাংশনটি ফিরে আসে।console.log('End')পুশ হয়, সম্পাদিত হয় এবং পপ হয়।- এখন কল স্ট্যাক খালি। ইভেন্ট লুপ টাইমার ফেজ পরীক্ষা করে এবং দেখে টাইমারটি মেয়াদোত্তীর্ণ হয়েছে। এটি কলব্যাকটিকে কল স্ট্যাকে স্থানান্তর করে, যা
Timeoutলগ করে।
এটি নন-ব্লকিং প্রকৃতি প্রদর্শন করে: টাইমারটি বাকি কোডকে ব্লক করে না।
ইভেন্ট লুপের ফেজগুলি
ইভেন্ট লুপ বিভিন্ন ফেজে কলব্যাক প্রক্রিয়া করে, প্রতিটির একটি নির্দিষ্ট উদ্দেশ্য থাকে। এই ফেজগুলি বোঝা আপনাকে সম্পাদনের ক্রম পূর্বাভাস দিতে সাহায্য করে।
| ফেজ | বিবরণ |
|---|---|
| টাইমার | setTimeout() এবং setInterval() দ্বারা নির্ধারিত কলব্যাক সম্পাদন করে। |
| পেন্ডিং কলব্যাক | পরবর্তী লুপ পুনরাবৃত্তিতে স্থগিত I/O কলব্যাক সম্পাদন করে। |
| আইডল, প্রিপেয়ার | Node.js অভ্যন্তরীণভাবে ব্যবহৃত হয়। |
| পোল | নতুন I/O ইভেন্ট পুনরুদ্ধার করে; I/O সম্পর্কিত কলব্যাক সম্পাদন করে (প্রায় সবগুলি টাইমার, setImmediate এবং ক্লোজ কলব্যাক ছাড়া)। |
| চেক | setImmediate() দ্বারা নির্ধারিত কলব্যাক সম্পাদন করে। |
| ক্লোজ কলব্যাক | ক্লোজ কলব্যাক সম্পাদন করে, যেমন socket.on('close', ...)। |
প্রতিটি ফেজের মধ্যে, Node.js মাইক্রোটাস্ক পরীক্ষা করে: process.nextTick() কলব্যাক এবং Promises। এগুলির উচ্চ অগ্রাধিকার থাকে এবং বর্তমান অপারেশন সম্পন্ন হওয়ার সাথে সাথে পরবর্তী ফেজে যাওয়ার আগে সম্পাদিত হয়।
মাইক্রোটাস্ক: nextTick এবং Promises
মাইক্রোটাস্ক ইভেন্ট লুপ ফেজের অংশ নয়; এগুলি প্রতিটি ফেজের পরে এবং প্রতিটি কলব্যাকের পরে প্রক্রিয়া করা হয়। এটি তাদের টাইমার এবং I/O কলব্যাকের আগে চালায়। উদাহরণস্বরূপ:
setTimeout(() => console.log('Timeout'), 0);
Promise.resolve().then(() => console.log('Promise'));
process.nextTick(() => console.log('nextTick'));
console.log('Sync');
আউটপুট: Sync, nextTick, Promise, Timeout। সিঙ্ক্রোনাস কোড প্রথমে চলে, তারপর মাইক্রোটাস্ক (Promises-এর আগে nextTick), তারপর টাইমার।
সতর্কতা: পুনরাবৃত্ত process.nextTick() কল ইভেন্ট লুপকে অনাহারে ফেলতে পারে, I/O ঘটতে বাধা দেয়। পুনরাবৃত্ত অপারেশনের জন্য setImmediate() ব্যবহার করুন।
setImmediate বনাম setTimeout
setImmediate() বর্তমান পোল ফেজ সম্পন্ন হওয়ার সাথে সাথে একটি কলব্যাক সম্পাদন করার জন্য ডিজাইন করা হয়েছে। বিপরীতে, 0ms সহ setTimeout() পরবর্তী টাইমার ফেজের জন্য অপেক্ষা করে। তাদের মধ্যে ক্রম প্রসঙ্গের উপর নির্ভর করে পরিবর্তিত হতে পারে, কিন্তু একটি I/O কলব্যাকের ভিতরে, setImmediate() সর্বদা প্রথমে চলে।
আপনার কোডের জন্য এটি কেন গুরুত্বপূর্ণ
ইভেন্ট লুপ বোঝা আপনাকে সাধারণ সমস্যাগুলি এড়াতে সাহায্য করে:
- ইভেন্ট লুপ ব্লক করবেন না:
fs.readFileSyncবা ভারী গণনার মতো সিঙ্ক্রোনাস অপারেশনগুলি পুরো লুপ ব্লক করে, আপনার অ্যাপকে অপ্রতিক্রিয়াশীল করে তোলে। অ্যাসিঙ্ক্রোনাস সংস্করণ ব্যবহার করুন বা ওয়ার্কার থ্রেডে অফলোড করুন। - মাইক্রোটাস্ক বুদ্ধিমানের সাথে ব্যবহার করুন: এগুলি I/O-এর আগে চলে, তাই ভারী মাইক্রোটাস্ক প্রক্রিয়াকরণ I/O কলব্যাক বিলম্বিত করতে পারে।
- কলব্যাক ক্রম বুঝুন: টাইমার, Promises এবং I/O মিশ্রিত করার সময়, আউটপুট পূর্বাভাস দিতে ফেজগুলি জানুন।
ব্যবহারিক উদাহরণ: নন-ব্লকিং ফাইল রিড
এখানে ইভেন্ট লুপ কীভাবে নন-ব্লকিং I/O সক্ষম করে:
const fs = require('fs');
console.log('Before read');
fs.readFile('large-file.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log('File read complete');
});
console.log('After read');
আউটপুট: Before read, After read, তারপর File read complete। ফাইল রিড ব্যাকগ্রাউন্ডে ঘটে এবং সম্পন্ন হলে কলব্যাকটি কিউতে যোগ হয়। এদিকে, অন্যান্য কোড চলতে পারে।
সাধারণ জিজ্ঞাসা
Node.js কি সত্যিই একক-থ্রেডেড?
Node.js আপনার জাভাস্ক্রিপ্ট একটি একক থ্রেডে চালায়, কিন্তু এটি ফাইল I/O, DNS এবং অন্যান্য অপারেশনের জন্য libuv থ্রেড পুলে একাধিক থ্রেড ব্যবহার করে। তাই এটি অভ্যন্তরীণভাবে সম্পূর্ণ একক-থ্রেডেড নয়।
কল স্ট্যাক এবং ইভেন্ট লুপের মধ্যে পার্থক্য কী?
কল স্ট্যাক সিঙ্ক্রোনাস কোড সম্পাদন করে। ইভেন্ট লুপ অ্যাসিঙ্ক্রোনাস কলব্যাক পরিচালনা করে, খালি হলে সেগুলি কিউ থেকে কল স্ট্যাকে স্থানান্তর করে।
আমি কি একাধিক ইভেন্ট লুপ তৈরি করতে পারি?
না, প্রতিটি Node.js প্রক্রিয়ার একটি ইভেন্ট লুপ থাকে। তবে, আপনি ওয়ার্কার থ্রেড ব্যবহার করে আলাদা জাভাস্ক্রিপ্ট থ্রেড চালাতে পারেন, প্রতিটির নিজস্ব ইভেন্ট লুপ সহ।
আপনি যদি পারফরম্যান্স সমস্যা ডিবাগ করতে সার্ভার লগ নিয়ে কাজ করেন, তাহলে লগ ডেটা দ্রুত পার্স এবং ভিজ্যুয়ালাইজ করতে আমাদের Nginx Log Analyzer ব্যবহার করে দেখুন।