আপনাকে বলা হয়েছে "প্রধান থ্রেড ব্লক করবেন না" এবং "আপনার দীর্ঘ কাজগুলি ভেঙে ফেলুন", কিন্তু এই জিনিসগুলি করার অর্থ কী?
জাভাস্ক্রিপ্ট অ্যাপ্লিকেশানগুলিকে দ্রুত রাখার জন্য সাধারণ উপদেশগুলি নিম্নোক্ত পরামর্শগুলিকে ফোটাতে থাকে:
- "প্রধান থ্রেড ব্লক করবেন না।"
- "আপনার দীর্ঘ কাজগুলি ভেঙে দিন।"
এটা মহান উপদেশ, কিন্তু এটা কি কাজ জড়িত? কম জাভাস্ক্রিপ্ট শিপিং ভাল, কিন্তু এটি কি স্বয়ংক্রিয়ভাবে আরও প্রতিক্রিয়াশীল ব্যবহারকারী ইন্টারফেসের সাথে সমান হয়? হতে পারে, কিন্তু হয়তো না।
জাভাস্ক্রিপ্টে কাজগুলি কীভাবে অপ্টিমাইজ করা যায় তা বোঝার জন্য, আপনাকে প্রথমে জানতে হবে কাজগুলি কী এবং ব্রাউজার কীভাবে সেগুলি পরিচালনা করে৷
একটি টাস্ক কি?
একটি টাস্ক হল ব্রাউজার যে কোন আলাদা কাজ করে। সেই কাজের মধ্যে রয়েছে রেন্ডারিং, HTML এবং CSS পার্স করা, JavaScript চালানো এবং অন্যান্য ধরনের কাজ যার উপর আপনার সরাসরি নিয়ন্ত্রণ নাও থাকতে পারে। এই সবের মধ্যে, আপনি যে জাভাস্ক্রিপ্ট লেখেন তা সম্ভবত কাজের সবচেয়ে বড় উৎস।
জাভাস্ক্রিপ্টের সাথে যুক্ত কার্যগুলি কয়েকটি উপায়ে কার্যক্ষমতাকে প্রভাবিত করে:
- যখন একটি ব্রাউজার স্টার্টআপের সময় একটি জাভাস্ক্রিপ্ট ফাইল ডাউনলোড করে, তখন এটি সেই জাভাস্ক্রিপ্ট পার্স এবং কম্পাইল করার জন্য কাজগুলিকে সারিবদ্ধ করে যাতে এটি পরে চালানো যায়।
- পৃষ্ঠার জীবনের অন্যান্য সময়ে, কাজগুলি সারিবদ্ধ থাকে যখন জাভাস্ক্রিপ্ট কাজ করে যেমন ইভেন্ট হ্যান্ডলারের মাধ্যমে ড্রাইভিং ইন্টারঅ্যাকশন, জাভাস্ক্রিপ্ট-চালিত অ্যানিমেশন এবং ব্যাকগ্রাউন্ড কার্যকলাপ যেমন বিশ্লেষণ সংগ্রহ।
এই সমস্ত স্টাফ- ওয়েব কর্মী এবং অনুরূপ APIগুলি বাদ দিয়ে-প্রধান থ্রেডে ঘটে।
মূল থ্রেড কি?
প্রধান থ্রেড হল যেখানে বেশিরভাগ কাজ ব্রাউজারে চলে এবং যেখানে আপনার লেখা প্রায় সমস্ত জাভাস্ক্রিপ্ট চালানো হয়।
প্রধান থ্রেড একটি সময়ে শুধুমাত্র একটি কাজ প্রক্রিয়া করতে পারে. 50 মিলিসেকেন্ডের বেশি সময় লাগে এমন যেকোনো কাজ একটি দীর্ঘ কাজ । 50 মিলিসেকেন্ডের বেশি কাজের জন্য, টাস্কের মোট সময় বিয়োগ 50 মিলিসেকেন্ড টাস্কের ব্লকিং সময়কাল হিসাবে পরিচিত।
ব্রাউজার যে কোনো দৈর্ঘ্যের একটি টাস্ক চলাকালীন ঘটতে পারস্পরিক ক্রিয়াগুলিকে অবরুদ্ধ করে, তবে এটি ব্যবহারকারীর পক্ষে উপলব্ধি করা যায় না যতক্ষণ পর্যন্ত কাজগুলি খুব বেশি সময় ধরে না চলে। যখন অনেক দীর্ঘ কাজ থাকা অবস্থায় একজন ব্যবহারকারী একটি পৃষ্ঠার সাথে ইন্টারঅ্যাক্ট করার চেষ্টা করে, তবে, ব্যবহারকারী ইন্টারফেসটি প্রতিক্রিয়াহীন বোধ করবে, এবং সম্ভবত এমনকি ভেঙে যেতে পারে যদি মূল থ্রেডটি খুব দীর্ঘ সময়ের জন্য অবরুদ্ধ থাকে।
মূল থ্রেডটিকে খুব বেশি সময় অবরুদ্ধ করা থেকে আটকাতে, আপনি একটি দীর্ঘ টাস্ককে কয়েকটি ছোট ছোট করে ভাগ করতে পারেন।
এটি গুরুত্বপূর্ণ কারণ যখন কাজগুলি বিচ্ছিন্ন হয়ে যায়, ব্রাউজারটি উচ্চ-অগ্রাধিকারমূলক কাজে অনেক তাড়াতাড়ি সাড়া দিতে পারে — ব্যবহারকারীর মিথস্ক্রিয়া সহ। পরবর্তীতে, অবশিষ্ট কাজগুলি শেষ হওয়ার দিকে চলে যায়, আপনি প্রাথমিকভাবে যে কাজটি সারিবদ্ধ করেছিলেন তা নিশ্চিত করে।
পূর্ববর্তী চিত্রের শীর্ষে, ব্যবহারকারীর মিথস্ক্রিয়া দ্বারা সারিবদ্ধ একটি ইভেন্ট হ্যান্ডলারকে এটি শুরু করার আগে একটি দীর্ঘ টাস্কের জন্য অপেক্ষা করতে হয়েছিল, এটি মিথস্ক্রিয়াটি ঘটতে বিলম্বিত করে। এই পরিস্থিতিতে, ব্যবহারকারী ল্যাগ লক্ষ্য করতে পারে. নীচে, ইভেন্ট হ্যান্ডলার শীঘ্রই চালানো শুরু করতে পারে, এবং মিথস্ক্রিয়া তাত্ক্ষণিক অনুভূত হতে পারে৷
এখন যেহেতু আপনি জানেন যে কেন কাজগুলি বিচ্ছিন্ন করা গুরুত্বপূর্ণ, আপনি জাভাস্ক্রিপ্টে এটি কীভাবে করবেন তা শিখতে পারেন।
টাস্ক ম্যানেজমেন্ট কৌশল
সফ্টওয়্যার আর্কিটেকচারে একটি সাধারণ উপদেশ হল আপনার কাজকে ছোট ফাংশনে বিভক্ত করা:
function saveSettings () {
validateForm();
showSpinner();
saveToDatabase();
updateUI();
sendAnalytics();
}
এই উদাহরণে, saveSettings()
নামে একটি ফাংশন আছে যা একটি ফর্ম যাচাই করতে, একটি স্পিনার দেখাতে, অ্যাপ্লিকেশন ব্যাকএন্ডে ডেটা পাঠাতে, ব্যবহারকারীর ইন্টারফেস আপডেট করতে এবং বিশ্লেষণ পাঠাতে পাঁচটি ফাংশন কল করে।
ধারণাগতভাবে, saveSettings()
ভালোভাবে আর্কিটেক্ট করা হয়েছে। আপনি যদি এই ফাংশনগুলির মধ্যে একটি ডিবাগ করতে চান তবে প্রতিটি ফাংশন কী করে তা নির্ধারণ করতে আপনি প্রজেক্ট ট্রিটি অতিক্রম করতে পারেন। এই ধরনের কাজ ব্রেক আপ প্রকল্পগুলি নেভিগেট এবং বজায় রাখা সহজ করে তোলে।
এখানে একটি সম্ভাব্য সমস্যা, যদিও, জাভাস্ক্রিপ্ট এই প্রতিটি ফাংশনকে আলাদা কাজ হিসাবে চালায় না কারণ সেগুলি saveSettings()
ফাংশনের মধ্যে কার্যকর করা হয়। এর মানে হল যে পাঁচটি ফাংশন একটি কাজ হিসাবে চলবে।
সর্বোত্তম ক্ষেত্রে, এমনকি শুধুমাত্র এই ফাংশনগুলির মধ্যে একটি কাজটির মোট দৈর্ঘ্যে 50 মিলিসেকেন্ড বা তার বেশি অবদান রাখতে পারে। সবচেয়ে খারাপ ক্ষেত্রে, এই কাজগুলির মধ্যে আরও অনেক বেশি সময় চলতে পারে-বিশেষ করে সংস্থান-সীমাবদ্ধ ডিভাইসগুলিতে।
ম্যানুয়ালি কোড এক্সিকিউশন পিছিয়ে দিন
একটি পদ্ধতি ডেভেলপাররা কাজগুলিকে ছোট করে ভাগ করার জন্য ব্যবহার করেছেন setTimeout()
। এই কৌশলটির সাহায্যে, আপনি ফাংশনটি setTimeout()
এ পাস করুন। এটি একটি পৃথক টাস্কে কলব্যাকের সম্পাদন স্থগিত করে, এমনকি যদি আপনি 0
এর সময়সীমা নির্দিষ্ট করেন।
function saveSettings () {
// Do critical work that is user-visible:
validateForm();
showSpinner();
updateUI();
// Defer work that isn't user-visible to a separate task:
setTimeout(() => {
saveToDatabase();
sendAnalytics();
}, 0);
}
এটি yielding নামে পরিচিত, এবং এটি ক্রমাগতভাবে চালানোর প্রয়োজন এমন একটি সিরিজের ফাংশনের জন্য সবচেয়ে ভাল কাজ করে।
যাইহোক, আপনার কোড সবসময় এই ভাবে সংগঠিত নাও হতে পারে। উদাহরণস্বরূপ, আপনার কাছে প্রচুর পরিমাণে ডেটা থাকতে পারে যা একটি লুপে প্রসেস করা দরকার, এবং যদি অনেকগুলি পুনরাবৃত্তি থাকে তবে সেই কাজটি খুব দীর্ঘ সময় নিতে পারে।
function processData () {
for (const item of largeDataArray) {
// Process the individual item here.
}
}
এখানে setTimeout()
ব্যবহার করা ডেভেলপার এরগনোমিক্সের কারণে সমস্যাযুক্ত, এবং প্রতিটি পৃথক পুনরাবৃত্তি দ্রুত চললেও ডেটার সম্পূর্ণ অ্যারে প্রক্রিয়া করতে খুব দীর্ঘ সময় নিতে পারে। এটি সব যোগ করে, এবং setTimeout()
কাজের জন্য সঠিক টুল নয়-অন্তত এইভাবে ব্যবহার করার সময় নয়।
ফলন পয়েন্ট তৈরি করতে async
/ await
ব্যবহার করুন
নিম্ন-অগ্রাধিকারমূলক কাজগুলির আগে গুরুত্বপূর্ণ ব্যবহারকারী-মুখী কাজগুলি নিশ্চিত করতে, আপনি ব্রাউজারকে আরও গুরুত্বপূর্ণ কাজ চালানোর সুযোগ দেওয়ার জন্য টাস্ক কিউতে সংক্ষিপ্তভাবে বাধা দিয়ে মূল থ্রেডে উঠতে পারেন।
যেমনটি আগে ব্যাখ্যা করা হয়েছে, setTimeout
প্রধান থ্রেডের জন্য ব্যবহার করা যেতে পারে। যদিও সুবিধার জন্য এবং ভাল পঠনযোগ্যতার জন্য, আপনি একটি Promise
মধ্যে setTimeout
কল করতে পারেন এবং কলব্যাক হিসাবে এর resolve
পদ্ধতিটি পাস করতে পারেন।
function yieldToMain () {
return new Promise(resolve => {
setTimeout(resolve, 0);
});
}
yieldToMain()
ফাংশনের সুবিধা হল যে আপনি যেকোনো async
ফাংশনে এটির জন্য await
করতে পারেন। পূর্ববর্তী উদাহরণটি তৈরি করে, আপনি চালানোর জন্য ফাংশনগুলির একটি অ্যারে তৈরি করতে পারেন এবং প্রতিটি চালানোর পরে মূল থ্রেডে ফলন করতে পারেন:
async function saveSettings () {
// Create an array of functions to run:
const tasks = [
validateForm,
showSpinner,
saveToDatabase,
updateUI,
sendAnalytics
]
// Loop over the tasks:
while (tasks.length > 0) {
// Shift the first task off the tasks array:
const task = tasks.shift();
// Run the task:
task();
// Yield to the main thread:
await yieldToMain();
}
}
ফলাফল হল যে একসময়ের একচেটিয়া কাজ এখন আলাদা আলাদা কাজে বিভক্ত হয়ে গেছে।
একটি ডেডিকেটেড সময়সূচী API
setTimeout
হল কার্যগুলি ভাঙার একটি কার্যকর উপায়, তবে এটির একটি ত্রুটি থাকতে পারে: আপনি যখন পরবর্তী টাস্কে চালানোর জন্য কোডটি বিলম্বিত করে মূল থ্রেডে যোগ দেন, তখন সেই টাস্কটি সারির শেষে যোগ করা হয়।
আপনি যদি আপনার পৃষ্ঠার সমস্ত কোড নিয়ন্ত্রণ করেন, তাহলে কাজগুলিকে অগ্রাধিকার দেওয়ার ক্ষমতা সহ আপনার নিজস্ব সময়সূচী তৈরি করা সম্ভব—কিন্তু তৃতীয় পক্ষের স্ক্রিপ্টগুলি আপনার সময়সূচী ব্যবহার করবে না৷ প্রকৃতপক্ষে, আপনি এই ধরনের পরিবেশে কাজকে অগ্রাধিকার দিতে সক্ষম নন। আপনি এটিকে শুধুমাত্র টুকরো টুকরো করতে পারেন, অথবা ব্যবহারকারীর ইন্টারঅ্যাকশনের জন্য স্পষ্টভাবে ফলন করতে পারেন।
শিডিউলার এপিআই postTask()
ফাংশন অফার করে যা কাজের সূক্ষ্ম সময়সূচীর জন্য অনুমতি দেয় এবং ব্রাউজারকে কাজের অগ্রাধিকার দিতে সাহায্য করার একটি উপায় যাতে কম অগ্রাধিকারের কাজগুলি মূল থ্রেডে আসে। postTask()
প্রতিশ্রুতি ব্যবহার করে, এবং তিনটি priority
সেটিংসের একটি গ্রহণ করে:
- সর্বনিম্ন অগ্রাধিকারমূলক কাজের জন্য
'background'
। - মাঝারি অগ্রাধিকারমূলক কাজের জন্য
'user-visible'
। কোনpriority
সেট না থাকলে এটি ডিফল্ট। -
'user-blocking'
গুরুত্বপূর্ণ কাজগুলির জন্য যা উচ্চ অগ্রাধিকারে চালানো প্রয়োজন।
একটি উদাহরণ হিসাবে নিম্নলিখিত কোডটি নিন, যেখানে postTask()
API সর্বোচ্চ সম্ভাব্য অগ্রাধিকারে তিনটি কাজ চালানোর জন্য এবং বাকি দুটি কাজ সর্বনিম্ন সম্ভাব্য অগ্রাধিকারে চালানোর জন্য ব্যবহৃত হয়।
function saveSettings () {
// Validate the form at high priority
scheduler.postTask(validateForm, {priority: 'user-blocking'});
// Show the spinner at high priority:
scheduler.postTask(showSpinner, {priority: 'user-blocking'});
// Update the database in the background:
scheduler.postTask(saveToDatabase, {priority: 'background'});
// Update the user interface at high priority:
scheduler.postTask(updateUI, {priority: 'user-blocking'});
// Send analytics data in the background:
scheduler.postTask(sendAnalytics, {priority: 'background'});
};
এখানে, কাজগুলির অগ্রাধিকার এমনভাবে নির্ধারিত হয় যাতে ব্রাউজার-অগ্রাধিকারযুক্ত কাজগুলি-যেমন ব্যবহারকারীর মিথস্ক্রিয়া-প্রয়োজন অনুসারে তাদের মধ্যে কাজ করতে পারে।
postTask()
কীভাবে ব্যবহার করা যেতে পারে তার এটি একটি সরল উদাহরণ। বিভিন্ন TaskController
অবজেক্টগুলিকে ইনস্ট্যান্ট করা সম্ভব যা প্রয়োজন অনুসারে বিভিন্ন TaskController
দৃষ্টান্তগুলির জন্য অগ্রাধিকার পরিবর্তন করার ক্ষমতা সহ কাজের মধ্যে অগ্রাধিকার ভাগ করতে পারে।
scheduler.yield()
API ব্যবহার করে ধারাবাহিকতা সহ অন্তর্নির্মিত ফলন
scheduler.yield()
একটি API বিশেষভাবে ব্রাউজারে প্রধান থ্রেড প্রদানের জন্য ডিজাইন করা হয়েছে। এর ব্যবহার yieldToMain()
ফাংশনের সাথে সাদৃশ্যপূর্ণ যা এই নির্দেশিকাতে পূর্বে প্রদর্শিত হয়েছে:
async function saveSettings () {
// Create an array of functions to run:
const tasks = [
validateForm,
showSpinner,
saveToDatabase,
updateUI,
sendAnalytics
]
// Loop over the tasks:
while (tasks.length > 0) {
// Shift the first task off the tasks array:
const task = tasks.shift();
// Run the task:
task();
// Yield to the main thread with the scheduler
// API's own yielding mechanism:
await scheduler.yield();
}
}
এই কোডটি মূলত পরিচিত, কিন্তু yieldToMain()
ব্যবহার করার পরিবর্তে, এটি await scheduler.yield()
ব্যবহার করে।
scheduler.yield()
এর সুবিধা হল ধারাবাহিকতা, যার মানে হল যে আপনি যদি একটি সেট কাজের মাঝখানে ফলন করেন, তবে অন্যান্য নির্ধারিত কাজগুলি ফলন পয়েন্টের পরে একই ক্রমে চলতে থাকবে। এটি তৃতীয় পক্ষের স্ক্রিপ্টের কোডকে আপনার কোডের সম্পাদনের ক্রমকে বাধাগ্রস্ত করা থেকে এড়ায়।
isInputPending()
ব্যবহার করবেন না
isInputPending()
API একজন ব্যবহারকারী একটি পৃষ্ঠার সাথে ইন্টারঅ্যাক্ট করার চেষ্টা করেছে কিনা তা পরীক্ষা করার একটি উপায় প্রদান করে এবং যদি একটি ইনপুট মুলতুবি থাকে তবেই ফল দেয়।
এটি জাভাস্ক্রিপ্টকে চালিয়ে যেতে দেয় যদি কোনও ইনপুট মুলতুবি না থাকে, ফলন না করে এবং টাস্ক কিউয়ের পিছনে শেষ হয়। এর ফলে প্রভাবশালী পারফরম্যান্সের উন্নতি হতে পারে, যেমনটি Intent to Ship- এ বিশদভাবে বলা হয়েছে, এমন সাইটগুলির জন্য যা অন্যথায় মূল থ্রেডে ফিরে আসতে পারে না।
যাইহোক, সেই API চালু হওয়ার পর থেকে, ফলন সম্পর্কে আমাদের বোঝা বেড়েছে, বিশেষ করে INP প্রবর্তনের মাধ্যমে। আমরা আর এই API ব্যবহার করার পরামর্শ দিই না , এবং পরিবর্তে বিভিন্ন কারণে ইনপুট মুলতুবি থাকুক বা না থাকুক না কেন ফলন দেওয়ার সুপারিশ করি:
- একজন ব্যবহারকারী কিছু পরিস্থিতিতে ইন্টারঅ্যাক্ট করা সত্ত্বেও
isInputPending()
ভুলভাবেfalse
ফেরত দিতে পারে। - ইনপুট একমাত্র ক্ষেত্রে নয় যেখানে কার্যগুলি পাওয়া উচিত। অ্যানিমেশন এবং অন্যান্য নিয়মিত ব্যবহারকারী ইন্টারফেস আপডেট একটি প্রতিক্রিয়াশীল ওয়েব পৃষ্ঠা প্রদানের জন্য সমানভাবে গুরুত্বপূর্ণ হতে পারে।
- এরপর থেকে আরও ব্যাপক ফলনকারী API চালু করা হয়েছে যা
scheduler.postTask()
এবংscheduler.yield()
মতো উদ্বেগের সমাধান করে।
উপসংহার
কাজগুলি পরিচালনা করা চ্যালেঞ্জিং, তবে এটি করা নিশ্চিত করে যে আপনার পৃষ্ঠা ব্যবহারকারীর ইন্টারঅ্যাকশনগুলিতে আরও দ্রুত সাড়া দেয়। কাজগুলি পরিচালনা এবং অগ্রাধিকার দেওয়ার জন্য কোনও একক পরামর্শ নেই, বরং বিভিন্ন কৌশল রয়েছে। পুনরাবৃত্ত করার জন্য, এইগুলি হল প্রধান জিনিসগুলি যা আপনি কাজগুলি পরিচালনা করার সময় বিবেচনা করতে চান:
- গুরুত্বপূর্ণ, ব্যবহারকারী-মুখী কাজগুলির জন্য মূল থ্রেডে যোগ দিন।
-
postTask()
দিয়ে কাজগুলোকে অগ্রাধিকার দিন। -
scheduler.yield()
নিয়ে পরীক্ষা করার কথা বিবেচনা করুন। - অবশেষে, আপনার ফাংশনে যতটা সম্ভব কম কাজ করুন।
এই টুলগুলির মধ্যে এক বা একাধিক দিয়ে, আপনি আপনার অ্যাপ্লিকেশনে কাজটি গঠন করতে সক্ষম হবেন যাতে এটি ব্যবহারকারীর চাহিদাগুলিকে অগ্রাধিকার দেয়, এবং নিশ্চিত করে যে কম সমালোচনামূলক কাজ এখনও সম্পন্ন হয়। এটি একটি ভাল ব্যবহারকারীর অভিজ্ঞতা তৈরি করতে যাচ্ছে যা আরও প্রতিক্রিয়াশীল এবং ব্যবহার করা আরও উপভোগ্য।
ফিলিপ ওয়ালটনকে বিশেষ ধন্যবাদ তার এই নির্দেশিকাটির প্রযুক্তিগত পরীক্ষা করার জন্য।
থাম্বনেইল ছবি আনস্প্ল্যাশ থেকে নেওয়া, আমিরালি মিরহাশেমিয়ানের সৌজন্যে।