Introduction
আজকের লেকচার বেসিক্যালি Asynchronous Programming নিয়ে। আজকের এজেন্ডাগুলো একটু দেখে নেয়া যাক।
- Understand Asynchronous Programming
- Event Loop
- Ways we can handle Asynchronous Tasks
- Callback
- Promise
- Async Await
- Async Iterator
- Async Generator
একে একে সব বিষয় আলোচনা করা হবে।
Understand Asynchronous Programming
ধরুন আপনি ব্যাংকে লাইনে দাঁড়িয়ে আছেন। সামনের জনের কাজ শেষ হলেই পরের জনের কাজ শুরু হবে। একে বলা হচ্ছে ব্লকিং। বর্তমান কাজ শেষ না হলে পরবর্তী কাজে যাওয়া যাবে না। আপনিও লাইনে দাঁড়িয়ে থাকতে থাকতে বিরক্ত হয়ে যাবেন।
বর্তমানে কিছু কিছু ব্যাংকে এমন সার্ভিস চালু হয়েছে, আপনি ঢুকবেন, একটা টোকেন কালেক্ট করবেন, এরপর ওয়েটিং লাউঞ্জে অপেক্ষা করবেন। আপনার সিরিয়াল যখন আসবে তখন আপনাকে ডাকা হবে। আপনার আর লাইনে দাঁড়িয়ে থাকতে হলো না। প্রথম সিস্টেমে আপনি ব্যাংকে ঢুকলে আর অন্য কোনো কাজ করা সম্ভব হতো না। কিন্তু এখন আপনি টোকেন নিয়ে বসে নেট ব্রাউজিং করতে পারছেন, ল্যাপটপে প্রয়োজনীয় কাজ সারতে পারছেন, বা বাইরে থেকে কিছু ছোট কাজ সেরে আসতেও পারছেন। কারণ আপনি আপনার সিরিয়াল জানেন, আর মোটামুটি কত সময় লাগতে পারে তাও আইডিয়া করতে পারছেন। এটাকে বলা হয় নন ব্লকিং। আর যে ওয়ে সেটাকে বলা হচ্ছে Asynchronous way।
আমরা সিনক্রোনাস প্রোগ্রামিং এর একটা উদাহরণ দিই।
console.log(1);
console.log(2);
console.log(3);
console.log(4);
console.log(5);
console.log(6);
console.log(7);
console.log(8);
console.log(9);
console.log(10);
এখানে ততক্ষণ পর্যন্ত ১০ এক্সিকিউট হবে না যতক্ষণ না ৯ পর্যন্ত এক্সিকিউট হয়। এটাকে বলে সিনক্রোনাস প্রোগ্রামিং। একটার পর একটা লাইন সিরিয়ালি এক্সিকিউট হবে।
অ্যাসিনক্রোনাস ওয়েটা হলো, একটা প্রসেস চলাকালীন আরেকটা কাজের রিকোয়েস্ট সে নিয়ে রাখবে। যেমন ব্যাংকে কেউ ৫০০০ টাকা তুলতে যায়, কেউ ৫ কোটি টাকা। যে ৫ হাজার তুলতে যায় তার কাজ কম, যে ৫ কোটি তুলতে যায় তার কাজ বেশি। আমাদের একটা ভুল ধারণা হচ্ছে অ্যাসিনক্রোনাস ওয়েতে সময় কম লাগে, আসলে সময় যেমন লাগার তেমনই লাগে। কিন্তু আমাদের রিকোয়েস্ট ব্লক হয় না। আমরা টোকেন নেয়ার মাধ্যমে একটা রিকোয়েস্ট দিচ্ছি। এরপর তার বর্তমান কাজ শেষ হলেই পরের কাজে যাবে। ব্যাকগ্রাউন্ডে কি চলছে সেটা ইউজার বুঝতে পারে না। অ্যাসিনক্রোনাস কাজ মূলত ব্যাকএন্ডেই বেশি, ফ্রন্টএন্ডে এর কাজ নেই বললেই চলে।
আরেকটু ভাল করে বুঝাই। ধরেন আমার একটা সার্ভার আছে। অনেকগুলো ক্লায়েন্ট রিকোয়েস্ট পাঠাবে। সার্ভারের মুখ প্রথমে ওপেন আছে। যেই একটা ক্লায়েন্ট থেকে রিকোয়েস্ট আসলো অমনি সার্ভারের মুখ ক্লোজ হয়ে গেলো। সার্ভারে সেই কাজটা সম্পন্ন হতে ধরেন ৫ সেকেন্ড লাগলো। ঐ ৫ সেকেন্ড সার্ভারের মুখ বন্ধ থাকবে। আর কোনো ক্লায়েন্ট থেকে রিকোয়েস্ট সেখানে ঢুকতে পারে না। আমরা রেজাল্ট দেখার সময় এই সমস্যায় পড়ি। রিফ্রেশ করেই যাই কিন্তু ঢুকা আর যায় না। কারণ একজনের রিকোয়েস্ট হ্যান্ডেল না হলে অন্যজনের রিকোয়েস্ট ঢুকতে পারছে না। একে বলে ব্লকিং।
আর নন ব্লকিং এর ক্ষেত্রে সার্ভার রিকোয়েস্ট ব্লক না করে একটা কিউ (Queue) তে রেখে দেয়। যত রিকোয়েস্ট আসবে সব গিয়ে কিউতে জমা হবে। এরপর সিরিয়াল ধরে ঐ কিউ থেকে রেসপন্স যার যার কাছে যাবে। এক্ষেত্রে কোনো রিকোয়েস্ট ব্লক হচ্ছে না। একে বলে নন ব্লকিং আর ওয়েটা হলো অ্যাসিনক্রোনাস ওয়ে। ছোট একটা কনসেপ্ট অনেক বড় বিপ্লব নিয়ে এসেছে প্রোগ্রামিং জগতে।
ল্যাঙ্গুয়েজ কখনও সিনক্রোনাস, অ্যাসিনক্রোনাস হতে পারে না। এই ফিচারটা থাকে ঐ ল্যাঙ্গুয়েজের যে কম্পাইলার বা রানটাইম থাকে সেখানে। জাভাস্ক্রিপ্টের ক্ষেত্রে v8 ইঞ্জিন হলো অ্যসিনক্রোনাস।
অ্যাসিনক্রোনাস টাস্কের একটা উদাহরণ দেখি আমরা।
console.log(1);
setTimeout(() => {
console.log(2);
}, 0);
setTimeout(() => {
console.log(3);
}, 0);
setTimeout(() => {
console.log(4);
}, 0);
setTimeout(() => {
console.log(5);
}, 0);
setTimeout(() => {
console.log(6);
}, 0);
setTimeout(() => {
console.log(7);
}, 0);
console.log(8);
যদিও এখানে টাইম দেয়া আছে ০, মানে কোনো অপেক্ষা করবে না, তাও setTimeout
থাকলেই সেই টাস্ক কিউতে যাবে। এবং অ্যাসিনক্রোনাস ওয়েতে কাজ করবে। তাহলে প্রথমে এক্সিকিউট হবে 1
, এরপর 8
, এরপর একে একে 2
, 3
, 4
, 5
, 6
, 7
।
আরেকটু ভালভাবে বুঝার জন্য আর একটা এক্সাম্পল দেখি।
function main() {
setTimeout(() => {
console.log('load last');
}, 10);
setTimeout(() => {
console.log('load first');
test();
}, 0);
test();
}
function test() {
console.log('test');
}
main();
এই কোডটা ভালভাবে ভিজ্যুয়ালাইজ করার জন্য আপনারা JavaScript Visualizer 9000 এ গিয়ে রান করতে পারেন।
এখানে প্রথমে main
ফাংশন কল স্ট্যাকে যাবে। এরপর main ফাংশনে যাওয়ার পর দেখবে দুইটা অ্যাসিনক্রোনাস টাস্ক আছে। সেই দুইটা চলে যাবে টাস্ক কিউতে। এরপর যাবে test এর কাছে। test চলে যাবে কল স্ট্যাকে। সেখানে থেকে test এক্সিকিউট হবে। test কল স্ট্যাক থেকে বের হয়ে যাবে। এরই সাথে main এরও কাজ শেষ, সেও কল স্ট্যাক থেকে বের হয়ে যাবে। এখন কল স্ট্যাক কিউ থেকে অ্যাসিনক্রোনাস টাস্কগুলোকে নিয়ে আসবে। প্রথমে আনবে যার এক্সিকিউশন টাইম কম তাকে। এক্ষেত্রে ০ এক্সিকিউশন টাইমের টাস্ককে কল স্ট্যাক নিয়ে আসবে। load first
প্রিন্ট হবে। এরপর সেখানে test ফাংশন পাওয়ার পর test কল স্ট্যাকে আসবে। test রান হবে। কল স্ট্যাক থেকে চলে যাবে। এরপর ১০ মিলিসেকেন্ডের টাস্ক কিউ থেকে কল স্ট্যাকে আসবে। সেটা এক্সিকিউট হবে।
তার মানে দেখা যাচ্ছে, সমস্ত সিনক্রোনাস টাস্ক প্রথমে কল স্ট্যাকে যাবে এবং সমস্ত অ্যাসিনক্রোনাস টাস্ক কিউতে যাবে। সিনক্রোনাস টাস্ক শেষ হওয়ার পর কল স্ট্যাক খালি হলে, কিউ থেকে কম এক্সিকিউশন টাইমের কাজ কল স্ট্যাকে আসবে এবং এক্সিকিউট হবে। এটাই অ্যাসিনক্রোনাস টাস্কের কনসেপ্ট। আপনারা কোডটা কপি করে উপরের সাইটে গিয়ে দেখলে আরো ক্লিয়ারলি বুঝতে পারবেন।
একটা জিনিস মাথায় রাখতে হবে অ্যাসিনক্রোনাস টাস্কের ভ্যালু আপনি কখনও ভ্যারিয়েবলে অ্যাসাইন করতে পারবেন না।
অ্যাসিনক্রোনাস প্রোগ্রামিং সম্পর্কে আরো জানতে Asynchronous JavaScript - Learn web development | MDN এই আর্টিকেলটা পড়ুন।
Event Loop
Event loop হলো, আমরা প্রথমে কোনো রিকোয়েস্ট কিউতে রেখে দিই, এরপর কল স্ট্যাক খালি হলে একটার পর একটা কিউ থেকে কল স্ট্যাকে পাঠাই। এই যে কিউ থেকে কল স্ট্যাকে পাঠানো এটা একটা লুপের মতো কাজ করে। আর এটাই ইভেন্ট লুপ। নিচের চিত্রটা দেখলে আরো পরিষ্কার হবে।
ইভেন্ট লুপ নিয়ে আরো জানতে The event loop - JavaScript | MDN, The JavaScript Event Loop: Explained - Towards Dev, What the heck is the event loop anyway? | Philip Roberts | JSConf EU এই আর্টিকেলগুলো পড়তে পারেন।
Ways we can handle Asynchronous Tasks
অ্যসিনক্রোনাস নিয়ে কাজ করতে গেলে আমাদের দুইটা প্রশ্নের উত্তর লাগবে।
- কখন আমাদের এই কোড এক্সিকিউট হবে?
- কোড এক্সিকিউট হওয়ার পর যে ডাটাগুলো পাবো সেগুলো আমরা কিভাবে হ্যান্ডেল করবো?
প্রথম প্রশ্নের উত্তর আমরা পেয়ে গেছি অলরেডি। দ্বিতীয় প্রশ্নের উত্তর নিচে আলোচনা করা হলোঃ
Callback
অ্যাসিনক্রোনাস টাস্ক হ্যান্ডেল করার জন্য একটা উপায় হলো কলব্যাক। কিন্তু কলব্যাকের একটা সমস্যা আছে। যেটার নাম কলব্যাক হেল। মানে একটা কলব্যাকের ভিতর আরেকটা কলব্যাক, সেটার ভিতর আরেকটা, সেটার ভিতর আরেকটা এভাবে করে চলতেই থাকবে যতক্ষণ না পর্যন্ত আপনি লাস্ট ডাটাটা পাচ্ছেন। এটা একটা বড় সমস্যা। সবচেয়ে বড় সমস্যা কোড লেখাও না, কোড পড়াও না, সবচেয়ে বড় সমস্যা হলো কোড ডিবাগ করা। আবার যেহেতু আমি প্রথম কলব্যাকের রেজাল্টটা কোথাও স্টোর করে রাখতে পারছি না তাই তার রেজাল্ট পাওয়ার জন্য আরেকটা কলব্যাক ব্যবহার করতেই হচ্ছে। তাই কলব্যাক অ্যাসিনক্রোনাস টাস্ক হ্যান্ডেল করার একটা সহজ উপায় হলেও আমরা কলব্যাক ব্যবহার করবো না।
ধরেন আমাদের একটা টাস্ক দেয়া হলো। এর ডিটেলস নিচে দেয়া হলোঃ
/**
* 1. find user by username
* 2. find post by userId
* 3. find latest post
* 4. find comments by post id
* 5. find latest comment
* 6. find username of the latest commented user
*/
আমাদের এমন কোনো API নাই যেখানে গিয়ে আমরা ইউজার নেইম দিয়ে দিবো আর সেই অনুসারে কমেন্টেড ইউজারের নাম দেখাবে। আমার সিস্টেমে অনেকগুলো ডিফারেন্ট API আছে। এই API গুলো থেকে খুঁজে আনতে হবে আমাদের। তো আমরা আমাদের API Endpoint গুলো লিখে ফেলি।
/**
* /users?username=[username]
* /posts?user_id=[user_id]
* /comments?post_id=[post_id]
* /users?username=[username]
*/
প্রথমে আমাদের username বের করে আনতে হবে। এখন username পেলে আমরা userid ও পাবো। সেটা দিয়ে latest post বের করে আনতে পারবো। এবার পোস্ট পেলে পোস্ট আইডি পাবো। সেই আইডি দিয়ে কমেন্ট বের করে আনতে পারবো। কমেন্ট থেকে ইউজার নেইম পাবো। এবার আমাদের প্রথম Endpoint এ আবার হিট করতে হবে। তার মানে মোট ৪টা অ্যাসিনক্রোনাস টাস্ক আছে এখানে। কিভাবে বুঝলাম আমরা এগুলো অ্যাসিনক্রোনাস টাস্ক। কারণ আমরা একটা সার্ভার থেকে আরেকটা সার্ভারে কমিউনিকেশন করছি। এক সার্ভার থেকে আরেক সার্ভারে কমিউনিকেশন করা অ্যাসিনক্রোনাস টাস্ক। এছাড়াও, setTimeout, setInterval, ফাইল রীড করা এসবও অ্যাসিনক্রোনাস টাস্ক।
function get(path, cb) {
const data = {}; // somehow process it
cb(data);
}
function getUserNameFromComment(username) {
get(`users?username=${username}`, (user) => {
get(`posts?user_id=${user.id}`, (posts) => {
get(`comments?post_id=${posts[0].id}`, (comments) => {
get(`users?username=${comments[0].username}`, (user) => {
console.log(user);
});
});
});
});
}
getUserNameFromComment('arif');
প্রথমে আমরা একটা ফাংশন নিলাম আমাদের ইউজার পাওয়ার জন্য। যেহেতু আমরা আগেই বলেছি অ্যাসিনক্রোনাস টাস্কের রেজাল্ট কোনো ভ্যারিয়েবলে স্টোর করা যায় না তাই আমাদের ডাটা পাওয়ার জন্য লাগবে একটা কলব্যাক ফাংশন। এবার আমরা আরেকটা ফাংশন বানালাম। get এর path হিসেবে দিলা আমাদের ইউজারনেম পাওয়ার এন্ডপয়েন্ট এবং আরেকটা কলব্যাক ফাংশন পোস্ট পাওয়ার জন্য। পোস্ট পাওয়ার পর আরেকটা ফাংশন বানালাম লেটেস্ট পোস্টের আইডি পাওয়ার জন্য। এরপর আবার আরেকটা ফাংশন বানালাম লেটেস্ট কমেন্ট কে করেছে তার নাম পাওয়ার জন্য। যেহেতু সেই নাম কোনো ভ্যারিয়েবলে স্টোর করতে পারবো না, তাই সেই নামটা প্রিন্ট করার জন্য আমাদের আরেকটা কলব্যাক ফাংশন বানাতে হবে। এখন চিন্তা করেন এখানে মাত্র ৪টা টাস্ক। যদি ১০০টা হয়, ১০০০টা হয় তখন আপনি কিভাবে ডিবাগ করবেন? আপনি পাগল হয়ে যাবেন। তাই কখনও আমরা এই কলব্যাক ব্যবহার করবো না। তাহলে এর চেয়ে বেটার সল্যুশন কি? চলুন দেখি।
Promise
Promise হলো জাভাস্ক্রিপ্টের একটা অবজেক্ট যার ভ্যালু ইনিশিয়ালি থাকবে না, কিন্তু ভবিষ্যতে আসবে। এটা resolve হতেও পারে নাও হতে পারে। এখন প্রমিজ কিভাবে ক্রিয়েট করা যায়? যেহেতু আমরা বলেছি জাভাস্ক্রিপ্টে প্রমিজ একটা অবজেক্ট সুতরাং সকল অবজেক্টের মতোই এর তৈরি করার সিনট্যাক্স একই new Promise()
। এই Promise এর মধ্যে একটা কলব্যাক ফাংশন থাকবে যার প্যারামিটার হিসেবে দুইটা জিনিস নিবো। resolve and reject. সাধারণত প্রমিজ বানানো হয় হয় রাখার জন্য নাহয় ভাঙার জন্য। রাখার জন্য হলে resolve আর ভাঙার জন্য হলে reject।
const isResolved = true;
const promise = new Promise((resolve, reject) => {
if (isResolved) {
resolve('completed');
} else {
reject('data');
}
});
console.log(promise); // Promise { 'completed' }
isResolved = true
হলে এরকম আউটপুট আসবে। কিন্তু যদি false হয় সে অনেক বড়সড় একটা এরর দেখাবে। এই এরর থেকে বাঁচতে আমরা catch
ব্লক ব্যবহার করতে পারি। প্রমিজের তিনটা ফাংশন থাকে। এগুলো হলোঃ
- then: যখন প্রমিজ resolve হয়ে যাবে তখন then ব্লক কল করবে।
- catch: যখন প্রমিজ কিছু resolve করতে পারবে না অর্থাৎ reject হবে তখন সেটা একটা এরর। সেই কাজটা হ্যান্ডেল করবে catch ব্লক।
- finally: প্রমিজ resolve বা reject যাই হোক না কেন লাস্ট একটা ব্লক কল করবেই, সেটা হলো finally।
promise
.then((result) => {
console.log(result);
})
.catch((e) => {
console.log('Rejected');
});
এভাবে ছাড়াও Promise.resolve()
, Promise.reject()
এভাবেও করা যায় সরাসরি। এখন প্রশ্ন আসতে পারে যদি সরাসরিই করা যায় আমাদের প্রমিজ বানানোর দরকার কি? কিছু কিছু API, functions আছে যারা আর্গুমেন্ট আকারে শুধু প্রমিজই নিবে, অন্য কিছু নিবে না। সেক্ষেত্রে আমাদের ডাটাকে প্রমিজ বানিয়ে ফেলতে হবে। প্রমিজ কিভাবে বানানো যায়। তা নিচের ছবিতে দেখুন।
Promise বানানোর পর যেভাবে আমরা প্রমিজের কাজগুলো করতে পারতাম তার সবই করতে পারবো। চলুন একটু দেখি।
আমরা একটা ছোটখাট টাইমার টাইপের অ্যাপলিকেশন বানানোর চেষ্টা করি।
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
wait(1000).then(() => {
console.log('Done in 1000ms');
});
wait(2000).then(() => {
console.log('Done in 2000ms');
});
wait(3000).then(() => {
console.log('Done in 3000ms');
});
এক সেকেন্ড পরপর তিনটা এক্সিকিউট হবে।
এবার আমরা কলব্যাকে যে রিকোয়ারমেন্টস নিয়ে কাজ করেছিলাম সেটা আমরা প্রমিজ দিয়ে কিভাবে করতে পারি সেটা দেখা যাক।
যদি একটা টাস্ক আরেকটা টাস্কের উপর ডিপেন্ডেন্ট হয় তাহলে প্রমিজের ক্ষেত্রে আমরা একটা চেইন ক্রিয়েট করতে পারি। কলব্যাকে যেভাবে একটার ভিতর আরেকটা কলব্যাক ব্যবহার করেছিলাম এখানে আমরা চেইন ক্রিয়েট করবো। ধরি আমাদের কাছে একটা ফাংশন আছে যেটা প্রমিজ রিটার্ন করে। যদি প্রমিজ রিটার্ন করে তাহলে আমরা then ফাংশনে যেতে পারবো, নাহয় যেতে পারব না।
const get = (url) => Promise.resolve(url);
get(`/users?username=anarul`)
.then((user) => {
/** do all other operations here */
return get(`/posts?user_id=${user.id}`);
})
.then((posts) => {
const latestPost = posts[0];
return get(`/comments?post_id=${latestPost.id}`);
})
.then((comments) => {
const latestComment = comments[0];
return get(`/users?username=${latestComment.username}`);
})
.then((user) => {
console.log(user);
})
.catch(() => {
console.log('Error');
});
এখানেও অনেক কাজ করতে হয়েছে। তবে কলব্যাকের তুলনায় এই কোডটা অনেক ভালভাবে পড়া যাচ্ছে। চিন্তা করেন কলব্যাকে প্রতিটা ফাংশনের জন্য যদি try catch ব্লক ব্যবহার করি তাহলে কতটা কষ্ট হবে আমাদের। সেখানে প্রমিজে আমরা মাত্র একটা catch ব্লক ব্যবহার করে আমাদের সমস্ত এরর হ্যান্ডেল করতে পারি। আর চেইন আকার হওয়ায় আমরা কোডটা সহজেই বুঝতে পারছি।
কিন্তু তাও এটাও অনেক কষ্টকর। খুব বেশি যে সহজ হয়ে গেলো তা না। আরো সহজ সল্যুশন আছে এটার চাইতে। চলুন দেখা যাক।
Async Await
Async Await এর ক্ষেত্রে প্রমিজ থাকলে সেটা then catch কিছু লেখার দরকার নাই। সরাসরি রেজাল্ট নিয়ে আসতে পারি। এর একটা শর্ত হচ্ছে async ফাংশন না হলে আমরা await করতে দিবো না। await এর মানেই হচ্ছে অপেক্ষা করা। Async Await হচ্ছে অনেকটা অ্যাসিনক্রোনাস প্রোগ্রামিং এর সিনক্রোনাস সিনট্যাক্স। দেখতে মনে হবে সিনক্রোনাস, কিন্তু কাজ হবে অ্যাসিনক্রোনাসের। কোনো ফাংশনকে async বানাতে হলে function কীওয়ার্ডের আগে জাস্ট async বসিয়ে দিলেই তা async ফাংশন হয়ে যাবে। এখন এই ফাংশন কিছু করুক বা না করুক একটা প্রমিজ রিটার্ন করবে। বিশ্বাস করার জন্য তো প্রমাণ দরকার। চলুন একটা প্রমাণ দেখাই।
যখন আমরা async কীওয়ার্ড ইউজ করিনি, তখন ফাংশন আমাদের undefined রিটার্ন করছে। কিন্তু যখন async ফাংশন লিখলাম তা আমাদের একটা প্রমিজ রিটার্ন করছে।
এবার আমরা আমাদের আগের টাস্ক Async Await দিয়ে করি।
const get = (url) => Promise.resolve(url);
async function getUserName(username) {
try {
const mainUser = await get(`/users?username=${username}`);
const posts = await get(`/posts?user_id=${mainUser.id}`);
const comments = await get(`/comments?post_id=${posts[0].id}`);
const user = await get(`/users?username=${comments[0].username}`);
console.log(user);
} catch (e) {
console.log(e);
}
}
যখনই ডাটা আসার ব্যাপার থাকবে তখন তা আসার জন্য কিছু সময় লাগবে। ঐ সময়টা যেন ব্লক হয়ে না থাকে তাই await দিয়ে বুঝানো হয় তোমার রিকোয়েস্ট প্রসেসিং হচ্ছে, একটু টাইম লাগবে। তুমি অপেক্ষা করো। যে ডাটা আসছে তা আমরা একেকটা ভ্যারিয়েবলে স্টোর করছি। সবশেষে ইউজার আসার পর আমরা তা প্রিন্ট করবো। এখানে দেখুন একটা try catch ব্লক দিয়ে কাজটা শেষ হয়ে যাচ্ছে। কোনো চেইন নেই। যেহেতু ভ্যারিয়েবলে আমরা ডাটা স্টোর করে রাখতে পারছি, আমরা ভ্যারিয়েবল ধরে ধরে ডিবাগ করতে পারি। খুব সহজেই পড়া যাচ্ছে। প্রমিজ, কলব্যাকে যেটা অনেক কাজ করতে হতো, এক্ষেত্রে অনেক কম কাজ করে অ্যাসিনক্রোনাস টাস্ক হ্যান্ডেল করা যায়।
এবার আমরা একটা রিয়েল লাইফ উদাহরণ দেখি। তার জন্য আমাদের axios প্যাকেজ ইনস্টল করে নিতে হবে এবং jsonPlacehlder থেকে ডাটা নিতে পারি।
const axios = require('axios').default;
const USERS_URL = 'https://jsonplaceholder.typicode.com/users';
const POSTS_URL = 'https://jsonplaceholder.typicode.com/posts';
const COMMENTS_URL = 'https://jsonplaceholder.typicode.com/comments';
async function getComments(username) {
try {
const { data: user } = await axios.get(`${USERS_URL}?username=${username}`);
const { data: posts } = await axios.get(
`${POSTS_URL}?userId=${user[0].id}`
);
const { data: comments } = await axios.get(
`${COMMENTS_URL}?postId=${posts[0].id}`
);
const { data: userWithComment } = await axios.get(
`${USERS_URL}?email=${comments[1].email}`
);
console.log(userWithComment);
} catch (error) {
console.log('Error Occurred', error.toJSON());
}
}
getComments('Bret');
প্রথমে আমরা ইউজার, পোস্ট এবং কমেন্টের URL কে ভ্যারিয়েবলে নিয়ে নিলাম। এরপর async ফাংশন বানালাম। সর্বপ্রথমে আমরা ইউজারনেইম দিয়ে ইউজার বের করে স্টোর করলাম। এরপর ঐ ইউজারের আইডি ব্যবহার করে সকল পোস্ট বের করে নিলাম। এরপর ঐ পোস্টগুলোর মধ্য থেকে প্রথম পোস্টের আইডি ব্যবহার করে কমেন্টগুলো বের করে নিলাম। তারপর প্রথম কমেন্টের থেকে আমরা ইউজার ইমেইল বের করে নিলাম। এরপর ঐ ইমেইল দিয়ে ইউজার বের করার জন্য হিট করলাম। কিন্তু কোনো ইউজার পাওয়া না যাওয়ার তা একটা ফাঁকা অ্যারে [] আউটপুট দিচ্ছে। এরর হ্যান্ডলিং এর জন্য try catch ব্লক ব্যবহার করেছি।
Async Iterator এবং Async Generator নিয়ে নেক্সট ক্লাসে আলোচনা করা হবে।
Source Code
এই লেকচারের সমস্ত সোর্স কোড এই লিংক এ পাবেন।