5.1 অ্যাসিঙ্ক্রোনাস জাভাস্ক্রিপ্ট কি ?

 

জাভাস্ক্রিপ্ট হল একটি সিঙ্গেল-থ্রেডেড প্রোগ্রামিং ল্যাঙ্গুয়েজ  যার মানে এক সময়ে শুধুমাত্র একটি কাজ করতে পারে। অর্থাৎ, জাভাস্ক্রিপ্ট ইঞ্জিন একটি সিঙ্গেল থ্রেডে একবারে একটিমাত্র স্টেটমেন্টকে প্রসেস  করতে পারে।  

যদিও সিঙ্গেল-থ্রেডেড ল্যাঙ্গুয়েজগুলি কোড লেখাকে সহজ করে কারণ আপনাকে concurrency  সংক্রান্ত সমস্যাগুলি নিয়ে চিন্তা করতে হবে না, যার অর্থ হল আপনি মেইন থ্রেডটি ব্লক না করে নেটওয়ার্ক অ্যাক্সেসের মতো দীর্ঘ সময় সাপেক্ষ কাজগুলি সম্পাদন করতে পারবেন না। অর্থাৎ, কোনো একটি দীর্ঘ বা  সময় সাপেক্ষ  কাজ  শেষ  না হওয়া পর্যন্ত আপনি মেইন থ্রেডকে দিয়ে অন্য কোনো কাজ করাতে পারবেন না।

ধরা যাক, একটি API থেকে কিছু ডেটার জন্য আপনি রিকোয়েস্ট করলেন। বিভিন্ন পরিস্থিতির উপর নির্ভর করে সার্ভারটি রিকোয়েস্টটি  প্রসেস করতে কিছু সময় নিতে পারে সেক্ষেত্রে মেইন থ্রেডটি ব্লক হয়ে থাকবে সার্ভার থেকে রেসপন্স আসার জন্য। ফলে সম্পূর্ণ  ওয়েব পেজটিই  প্রতিক্রিয়াহীন হয়ে থাকবে।

ঠিক এই ক্ষেত্রেই অ্যাসিঙ্ক্রোনাস জাভাস্ক্রিপ্টের প্রয়োজন হয়। অ্যাসিঙ্ক্রোনাস জাভাস্ক্রিপ্ট ব্যবহার করে (যেমন: কলব্যাক ফাংশন, প্রমিজ , এবং অ্যাসিঙ্ক/এওয়েট ), আপনি প্রধান থ্রেড ব্লক না করে দীর্ঘ নেটওয়ার্ক রিকোয়েস্টগুলি বা API কলের মতো সময় সাপেক্ষ কাজগুলো সম্পাদন করতে পারবেন। অর্থাৎ জাভাস্ক্রিপ্টের মতো সিঙ্গেল থ্রেডেড প্রোগ্রামিং ল্যাঙ্গুয়েজ দিয়ে মাল্টি থ্রেডেড ল্যাঙ্গুয়েজের  মতো করে কাজ করাতে পারবেন।

তাহলে চলুন আর কথা না বাড়িয়ে অ্যাসিঙ্ক্রোনাস জাভাস্ক্রিপ্ট নিয়ে বিস্তারিত আলোচনা শুরু  করা যাক।

সিঙ্ক্রোনাস জাভাস্ক্রিপ্ট কিভাবে কাজ করে?

আমরা অ্যাসিঙ্ক্রোনাস জাভাস্ক্রিপ্টে যাওয়ার আগে, প্রথমে জেনে নেওয়া দরকার কীভাবে সিঙ্ক্রোনাস জাভাস্ক্রিপ্ট কোড জাভাস্ক্রিপ্ট ইঞ্জিনের ভিতরে কাজ করে । উদাহরণ স্বরূপ:

const second = () => {
   console.log('Hello there!');
}
const first = () => {
   console.log('Hi there!');
   second();
   console.log('The End');
}
first();

 

উপরের কোডটি জাভাস্ক্রিপ্ট ইঞ্জিনের ভিতরে কীভাবে কাজ করে তা বোঝার জন্য, আমাদের এক্সিকিউশন কন্টেক্সট এবং কল স্ট্যাকের ধারণাটি বুঝতে হবেএক্সিকিউশন কন্টেক্সট এবং কল স্ট্যাক সস্পর্কে পরিষ্কার  ধারণা না থাকলে  অনুরোধ  করবো আমাদের এক্সিকিউশন কন্টেক্সট এবং কল স্ট্যাক নিয়ে আলোচনাটি ভালো করে দেখার জন্য। 

যখন এই কোডটি কার্যকর হয়, তখন একটি গ্লোবাল এক্সিকিউশন কন্টেক্সট তৈরি করা ( যা main() দ্বারা রিপ্রেসেন্ট করা হয়) এবং এই main() কে কল স্ট্যাকের শীর্ষে ঠেলে দেওয়া হয়। তারপর first() ফাংশনটি যখন কল হয় তখন এটিকে আবার  স্ট্যাকের উপরে ঠেলে দেওয়া হয়। তাহলে আমাদের কল স্ট্যাকে সবার উপরে আছে first() এবং পরে আছে main() ফাংশন। তারপর এটি first() এর ভিতরের কোড এক্সেকিউট করবে। অর্থাৎ  Hi there! এক্সেকিউট হবে। এর পরের লাইনে আরো একটি ফাংশন কল হয়েছে second(). তাই এই ফাংশনটিও আমাদের কল স্টেকের সবার উপর ঢুকবে। যেকোন একটু লক্ষ্য করুন,আমাদের কল স্টেকের সবার উপরে রয়েছে second().তাই second() এই ফাংশনের ভিতরের কোড এক্সেকিউট হবে। আর তাহলো ‘Hello there!’

এখন second() ফাংশনের সব স্টেটমেন্ট শেষ তাই কল স্ট্যাক থেকে second() ফাংশনটি ডিলিট হয়ে যাবে। এখন স্টেকের উপরে রয়েছে fitst() ফাংশন আর console.log(‘The End’);স্টেটমেন্টটি এখনও এক্সেকিউ হয় নি তাই এই স্টেটমেন্টটি এক্সেকিউট হবে। এখন first() ফাংশনটির কাজ শেষ তাই এটিও কল স্ট্যাক থেকে পপ হয়ে যাবে। আর প্রোগ্রামটি এই মুহুর্তে তার সব কাজ সম্পন্ন করেছে,তাই গ্লোবাল এক্সিকিউশন কন্টেক্সট ( main()) স্ট্যাক থেকে পপ হয়ে যাবে।

অ্যাসিঙ্ক্রোনাস জাভাস্ক্রিপ্ট কিভাবে কাজ করে?

এখন, সিঙ্ক্রোনাস জাভাস্ক্রিপ্ট কীভাবে কাজ করে সে সম্পর্কে আমাদের ধারণা আছে, আসুন অ্যাসিঙ্ক্রোনাস জাভাস্ক্রিপ্ট সম্পর্কে কথা বলি। এটি ব্যাখ্যা করতে, আসুন নীচের কোডটি দেখি:

console.log("Hello.");
setTimeout(function() {
  console.log("Goodbye!");
}, 3000);
console.log("Hello again!")


উপরের কোডটির আউটপুট কি হবে বলুন তো দেখি। জাভাস্ক্রিপ্ট ইঞ্জিন উপরের কোডটি সিঙ্ক্রোনাসভাবে কার্যকর করবে না। আসুন নীচের আউটপুটটি একবার দেখে নেওয়া যাক:

Hello
Hello again!
Goodbye!

 

কোডটিতে, আমাদের কনসোলে প্রথমে  Hello , এরপরে আমরা একটি ফাংশন লিখেছিলাম যা তিন সেকেন্ড পরে আমাদের কনসোলে Goodbye করবে এবং সবার শেষে আমাদের কোডের শেষ অংশটি Hello again! কনসোলে লগ করে দেখাবে। সাধারণত তাই হবার কথা  কিন্তু এখানে, জাভাস্ক্রিপ্ট ইঞ্জিন প্রথম Hello স্টেটমেন্টটির মধ্য দিয়ে যায় এবং এটিকে এক্সেকিউট  করে এবং কনসোলে প্রিন্ট করে, পরবর্তী লাইনে গিয়ে এটি একটি ফাংশন দেখে এবং ফাংশনটি প্রিন্ট করার জন্য তিন সেকেন্ড অপেক্ষা করার প্রয়োজন তাই জাভাস্ক্রিপ্ট এই ফাংশনটিকে আর কল স্ট্যাকে না রেখে এটিকে কলব্যাক  queue  তে  পাঠিয়ে দেয় এবং শেষ স্টেটমেন্টে চলে যায় এবং এটিকে এক্সেকিউট করে যা কনসোলে Hello again! প্রিন্ট করবে। এরপর যখন তিন সেকেন্ড হয়ে যায় তখন ইভেন্ট লুপ কলব্যাক queue থেকে সেই  setTimeout কে আবার কল স্ট্যাকে নিয়ে আসে এবং  এক্সেকিউট করে ফলে আমরা কনসোলে Goodbye  পাই। একটু লক্ষ্য করে দেখুন, আমরা কিন্তু তিন সেকেন্ড অপেক্ষা করে সময় নষ্ট করি নি আমরা এই সময়ের মধ্যে আমাদের কোডের বাকি কাজগুলো করে ফেলেছি আর যখন setTimeout এর কাজ হয়েছে তখন এটিকে আবার কল স্ট্যাকে এনে এক্সেকিউট করেছি ।

তাই অ্যাসিঙ্ক্রোনাস জাভাস্ক্রিপ্টে, একটি ফাংশন চালানোর সময় জাভাস্ক্রিপ্ট ফাংশনটির রেস্পন্সের জন্য অপেক্ষা করে না, বরং এটি পরবর্তী  ফাংশনগুলি এক্সেকিউট করতে থাকে ।

উদাহরন
  • একটি async ফাংশন হল async কীওয়ার্ডের সাথে ঘোষিত একটি ফাংশন এবং এটির মধ্যে await কীওয়ার্ড অনুমোদিত। async এবং await keyword গুলি asynchronous, promise-based আচরণকে একটি পরিষ্কার শৈলীতে লিখতে সক্ষম করে, স্পষ্টভাবে promise চেইনগুলি কনফিগার করার প্রয়োজন এড়াতে।
function resolveAfter2Seconds() {
 return new Promise((resolve) => {
   setTimeout(() => {
     resolve("resolved");
   }, 2000);
 });
}
 
async function asyncCall() {
 console.log("calling");
 const result = await resolveAfter2Seconds();
 console.log(result);
 // expected output: "resolved"
}

উপরের এর output নিচে দেওয়া হল।

calling
resolved

 

  • নিম্নলিখিত উদাহরণে, আমরা ধারাবাহিকভাবে দুটি await promise করছি। অগ্রগতি তিনটি পর্যায়ে ফাংশন foo() এর মাধ্যমে চলে।
async function foo() {
 const result1 = await new Promise((resolve) =>
   setTimeout(() => resolve("1"))
 );
 const result2 = await new Promise((resolve) =>
   setTimeout(() => resolve("2"))
 );
}
foo();
    1. foo ফাংশনের বডির প্রথম লাইনটি promise এর সাথে await এক্সপ্রেশন কনফিগার করা সহ synchronous ভাবে কার্যকর করা হয়। foo এর মাধ্যমে অগ্রগতি তখন স্থগিত করা হয় এবং নিয়ন্ত্রণ ফাংশনে ফিরে আসে যাকে foo বলা হয়।

 

    1. কিছু সময় পরে, যখন প্রথম promise পূর্ণ হয় বা প্রত্যাখ্যান করা হয়, নিয়ন্ত্রণ আবার foo() তে চলে যায়। প্রথম promise পূর্ণতার ফলাফল (যদি এটি প্রত্যাখ্যান না করা হয়) await অভিব্যক্তি থেকে ফিরে আসে। এখানে result1 এর জন্য 1 বরাদ্দ করা হয়েছে। অগ্রগতি অব্যাহত, এবং দ্বিতীয় await অভিব্যক্তি মূল্যায়ন করা হয়. আবার, foo-এর মাধ্যমে অগ্রগতি স্থগিত করা হয় এবং নিয়ন্ত্রণ পাওয়া যায়।

 

  1. কিছু সময় পরে, যখন দ্বিতীয় promise পূর্ণ হয় বা প্রত্যাখ্যান করা হয়, তখন নিয়ন্ত্রণ foo তে পুনরায় প্রবেশ করে। দ্বিতীয় promise রেজোলিউশনের ফলাফল দ্বিতীয় awaitর অভিব্যক্তি থেকে ফিরে আসে। এখানে result2 এর জন্য 2 বরাদ্দ করা হয়েছে।

 

এসো নিজে করি
  • একটি async function লিখ যেটি একটি promise রিটার্ন করবে।
  • এমন একটি async function লিখ যেটি ৩ সেকেন্ড পর promise return করবে।
  • নিচের কোডটির আউটপুট কি হবে তা স্টেপ বাই স্টেপ বুঝিয়ে বলতে হবে?
console.log("Hello World");

setTimeout(()=>{
   console.log("Hi");
})

console.log("Hello World Again");