5.4 Event loop (ইভেন্ট লুপ) কি ?

 

Event loop (ইভেন্ট লুপ) জাভাস্ক্রিপ্টের একটি সিক্রেট machenism যার মাধ্যমে জাভাস্ক্রিপ্ট single-threaded প্রোগ্রামিং ল্যাঙ্গুয়েজ হওয়া সত্ত্বেও বাহ্যিক ভাবে multi-threaded প্রোগ্রামিং ল্যাঙ্গুয়েজের মত কাজ করে। ইভেন্ট লুপ গভীরভাবে call stack কে পর্যবেক্ষণ করে এবং যদি call stack খালি বা empty থাকে তবে Event queue থেকে Task call stack এ পাঠায় execution সম্পন্ন করার জন্য।

আমরা সকলে জানি, জাভাস্ক্রিপ্ট একটি সিঙ্গেল-থ্রেডেড asynchronous প্রোগ্রামিং ল্যাঙ্গুয়েজ । এইটা বিষয় লক্ষ্য করেছেন কি, একটা ল্যাঙ্গুয়েজ কি করে একই সাথে সিঙ্গেল-থ্রেডেড আবার asynchronous  হতে পারে? আসলে বিষয়টা হল, জাভাস্ক্রিপ্ট একটা সিঙ্গেল-থ্রেডেড ল্যাঙ্গুয়েজ; তার মানে হল জাভাস্ক্রিপ্ট একসাথে একই সময়ে একটা মাত্র কাজ  করতে পারে। আর asynchronous বিষয়টা জাভাস্ক্রিপ্ট ল্যাঙ্গুয়েজের কোন বিষয় না, এটি নিয়ন্ত্রিত হয় ব্রাউজার Enviornment এর মাধ্যমে। কয়েকটি উদাহরন দেখলে বিষয়টা বোঝতে সুবিধা হবে। চলুন শুরু করা যাক।

function main() {
   console.log('Hi');
   setTimeout(function display() {
     console.log('there');
   }, 1000);
   console.log('JSConfEU');
}
main();


// Output
// A
// C
// B

 

উপরের কোডটি একটু ভাল করে লক্ষ্য করুন, এখানে প্রথম  console.log(‘Hi’) এই স্টেটমেন্ট টি execute হচ্ছে। এরপর একটা setTimeout ফাংশন রয়েছে, যা নিদিষ্ট সময় পর console.log(‘there’) এই স্টেটমেন্ট টি execute করার কথা ছিল। কিন্তু কোন কারণে এই স্টেটমেন্ট টিকে বাদ দিয়ে তারপরের স্টেটমেন্ট  console.log(‘JSConfEU’) এইটিকে execute করছে। এইটির কারণ হল জাভাস্ক্রিপ্টের asynchronous ব্যবহার। জাভাস্ক্রিপ্ট যখনই setTimeout ফাংশনটিকে দেখছে, ঠিক তখনই জাভাস্ক্রিপ্ট বোঝতে পারছে এটি একটি asynchronous ফাংশন । আর আমরা জানি asynchronous ফাংশনের কাজ শেষ হতে কিছু সময়ের প্রয়োজন হয়। তাই জাভাস্ক্রিপ্ট setTimeout ফাংশনটিকে আর call stack এ না রেখে, Browser API এ পাঠিয়ে দেয়। আর এই Browser API তেই setTimeout ফাংশনটি তার নির্ধারিত সময় নিয়ে কাজ টি শেষ করে। কাজ শেষ হবার পর setTimeout ফাংশনটি Event Queue তে প্রবেশ করে। এবং পুনরায় call stack এ প্রবেশ করার অপেক্ষা করে। ঠিক এখানে এ ইভেন্ট লুপ এর কাজ শুরু হয়। ইভেন্ট লুপ এর কাজ হল Call Stack আর Event Queue এর দিকে লক্ষ্য রাখা। যখন Call Stack খালি হয়ে যায় ঠিক তখন ইভেন্ট লুপ Event Queue থেকে একটি একটি করে Event call stack এ পাঠায়। এবং ইভেন্ট লুপ ওই অনুযায়ী স্টেটমেন্ট Execute করতে থাকে। এই প্রোগ্রামের ক্ষেত্রে console.log(‘there’) স্টেটমেন্টটি Execute হয়। আর যখন setTimeout ফাংশনটিকে যখন call stack থেকে Browser API তে পাঠানো হয় তখন call stack টি খালি থাকার কারণে জাভাস্ক্রিপ্ট console.log(‘JSConfEU’) স্টেটমেন্টটিকে call stack এ পাঠিয়ে execute করে ফেলে যার কারণে console.log(‘JSConfEU’) স্টেটমেন্টটি console.log(‘there’) এর আগে execute করছে।

জাভাস্ক্রিপ্টে setTimeout ছাড়া আরও কিছু ফাংশন আছে যা asynchronous API রয়েছে। যেমনঃ addEventListener, setTimeout, setInterval, যেকোনো ধরনের API কল।

নিচের Graphical Representation টা দেখলে বিষয়টি আরও পরিষ্কার হবে।

Event Loop 5.4 Event loop (ইভেন্ট লুপ) কি?

উদাহরন

এতক্ষনতো আমরা Event Loop সম্পর্কে জানলাম, আসুন এখন কিছু উদাহরণ দেখি।

  • যখন আমরা setTimeout() ফাঙ্কশনটি ব্যবহার করি এর অর্থ আমরা ওই কোড ব্লকটিকে asynchronous হিসাবে ডিক্লেয়ার করেছি। জাভাস্ক্রিপ্ট এখন setTimeout কে webapi এ পাঠাবে এবং webapi এ কাজ শেষ হলে তা callback queue এ আসবে। Callback Queue থেকে Event Loop এর মাধ্যমে কল স্ট্যাকে আসবে।
console.log("First console");
setTimeout(() => {
console.log("First setTimeout");
}, 1000);

console.log("Second console");

setTimeout(() => {
console.log("Second setTimeout");
}, 0);

/*
Output:
First console
Second console
Second setTimeout
First setTimeout
*/

 

  • এর আগের উদাহরনে আমরা setTimeout ফাংশনের জন্য Event Loop কিভাবে কাজ করেছে দেখেছি। এবার প্রমিস ব্যবহার করে দেখব।
    প্রমিস setTimeout এর মতো callback queue তে না গিয়ে micro-task queue তে যায়। এটাও callback queue এর মতোই কাজ করে কিন্তু callback queue থেকে প্রায়োরিটি বেশি setTimeout এর।  ডিলেটাইম  শূন্য সেকেণ্ড হলেও callback queue তে বসে থাকতে হবে যতক্ষণ call stack খালি না হয়।
firstPromise = new Promise((resolve) => {
resolve("First promise.")
});
secondPromise = new Promise((resolve) => {
resolve("Second promise.")
});
console.log("First console.")
setTimeout(() => console.log("First setTimeout."), 1000);
setTimeout(() => console.log("Second setTimeout."));
firstPromise.then(response => console.log(response));
secondPromise.then(response => console.log(response));

/*
Output:
First console.
First promise.
Second promise.
Second setTimeout.
First setTimeout.
*/
  • While লুপ ৫ সেকেন্ড পর তার কাজ শেষ করলে callstack খালি হলে callback queue থেকে Event Loop এর মাধ্যমে setTimeout এর কলব্যাক ফাংশনটি  callstack এ এসে কাজ সম্পন্য করবে।
function myFunc (){
console.log('first')
setTimeout(function hello(){
console.log('second')
}, 0)
runforNSceconds(5)
console.log('third')
}

myFunc()

function runforNSceconds(sec){
let start = Date.now(), now = start;
while(now-start< (sec*1000)){
now = Date.now()
 }
}
/*
Output:
first
third
second
*/
  • নিচের উদাহরণে Event Loop নম্বর কাউন্টিং এর মাধম্যে দেখানো হয়েছে। যদি তুমি নম্বরের ভ্যালু সব এক হলো কেন বুঝতে না পারো তবে স্কোপিং এর চাপ্টারটি আবার রিভিশন দিয়ে আস।
function main(number) {
console.log('Lets count from 0 to ' + number);
for (var i = 0; i <= number; i++) {
setTimeout(function () {
console.log(i + '\n');
}, 1000);
}
console.log('Done!');
}

main(5);

//এখানে output হবে এমন:
// "Lets count from 0 to 5"
// "Done!"
// "6
// "
// "6
// "
// "6
// "
// "6
// "
// "6
// "
// "6

 

এসো নিজে করি
  • নিচের কোড স্নিপেটের আউটপুটের ফ্লোটি ব্যাখ্যা করুন।
var promise = new Promise((resolve) => {
  resolve("Promise.");
});
 
setTimeout(() => console.log("First setTimeout."), 0);
 
promise.then((res) => {
  console.log(res);
});
 
setTimeout(() => console.log("Second setTimeout."),0);
console.log("First console.");

 

  • নিচের কোড স্নিপেটের আউটপুট কি হবে?
var promise = new Promise((resolve) => {
  resolve("Promise.");
});
 
setTimeout(() => console.log("First setTimeout."), 0);
 
promise.then((res) => {
  setTimeout(() => console.log(res), 0);
});
 
setTimeout(() => console.log("Second setTimeout."),0);
console.log("First console.");
  • নিচের কোড স্নিপেটের আউটপুট কি হবে?
setTimeout(function(){
    console.log("first message")
    setTimeout(function(){
        console.log("second message")
    }, 1000)
}, 0)
runforNSceconds(3)
console.log("third message")
 
let promise = new Promise(function(resolve, reject){
    resolve()
})
 
promise.then(function(resolve){
    console.log("fourth message from promise 1")
})
.then(function(promise){
    console.log("fifth message from promise 2")
})
 
console.log("sixth message")
 
function runforNSceconds(sec){
    let start = Date.now(), now = start;
    while(now-start< (sec*1000)){
        now = Date.now()
    }
}
  • নিচের কোড স্নিপেটের আউটপুট কি হবে?
function main(number) {
    console.log('Lets count from 0 to ' + number);
    for (let i = 0; i <= number; i++) {
        setTimeout(function () {
            console.log(i + '\n');
        }, 1000);
    }
    console.log('Done!');
}
 
main(3);