আগের পর্বে আমরা localStorage-এ Class instance সংরক্ষণ শিখেছিলাম। এই পর্বে আমরা দেখব Error Handling বিস্তারিতভাবে — try/catch/finally-এর সঠিক ব্যবহার, আর কাস্টম এরর ক্লাস বানানো — যা পর্ব ৮-এর Inheritance-এর একটা বাস্তব প্রয়োগ।
try/catch/finally রিক্যাপ
বেসিক স্ট্রাকচার মনে করিয়ে দেওয়া যাক — try-তে যে কোড এরর দিতে পারে সেটা রাখা হয়, catch-এ এরর হ্যান্ডেল করা হয়, আর finally-তে যা লেখা থাকে সেটা এরর হোক বা না হোক, সবসময় রান হয়।
function divide(a, b) {
try {
if (b === 0) {
throw new Error("শূন্য দিয়ে ভাগ করা যায় না");
}
return a / b;
} catch (error) {
console.log("এরর ধরা পড়েছে:", error.message);
return null;
} finally {
console.log("এই লাইনটা সবসময় রান হবে, এরর হোক বা না হোক");
}
}
divide(10, 2); // ৫ রিটার্ন করবে, finally-ও রান হবে
divide(10, 0); // null রিটার্ন করবে, catch আর finally দুটোই রান হবে
finally-এর সবচেয়ে সাধারণ ব্যবহার: ক্লিনআপ কাজ — যেমন লোডিং স্পিনার বন্ধ করা, ফাইল হ্যান্ডেল বন্ধ করা, বা কানেকশন ছেড়ে দেওয়া — এসব এরর হোক বা না হোক করতেই হবে।
async function loadData() {
showLoadingSpinner();
try {
const data = await fetch("/api/data");
return data.json();
} catch (error) {
showErrorMessage(error.message);
} finally {
hideLoadingSpinner(); // সফল হোক বা ব্যর্থ, স্পিনার বন্ধ করতেই হবে
}
}
কাস্টম Error ক্লাস — Inheritance-এর বাস্তব প্রয়োগ
শুধু new Error("বার্তা") ব্যবহার করলে সব এরর একই রকম দেখায়। JavaScript-এর বিল্ট-ইন Error ক্লাসকে extend করে নিজের মতো বিশেষায়িত এরর টাইপ বানানো যায় — যা আগের পর্বের extends/super-এরই বাস্তব প্রয়োগ।
class ValidationError extends Error {
constructor(message, field) {
super(message); // Error ক্লাসের constructor কল করা
this.name = "ValidationError"; // এরর টাইপ চেনার জন্য নাম দেওয়া
this.field = field; // অতিরিক্ত কাস্টম তথ্য
}
}
class NetworkError extends Error {
constructor(message, statusCode) {
super(message);
this.name = "NetworkError";
this.statusCode = statusCode;
}
}
function validateEmail(email) {
if (!email.includes("@")) {
throw new ValidationError("ইমেইল সঠিক না", "email");
}
}
try {
validateEmail("wrong-email");
} catch (error) {
console.log(error.name); // "ValidationError"
console.log(error.message); // "ইমেইল সঠিক না"
console.log(error.field); // "email" — কাস্টম প্রপার্টি
}
একাধিক এরর টাইপ আলাদাভাবে হ্যান্ডেল করা
কাস্টম এরর ক্লাসের সবচেয়ে বড় সুবিধা — instanceof দিয়ে চেক করে ভিন্ন ভিন্ন এরর টাইপের জন্য ভিন্ন ভিন্ন প্রতিক্রিয়া দেওয়া যায়।
async function submitForm(data) {
try {
validateEmail(data.email);
const response = await fetch("/api/submit", { method: "POST", body: JSON.stringify(data) });
if (!response.ok) {
throw new NetworkError("সার্ভার এরর", response.status);
}
} catch (error) {
if (error instanceof ValidationError) {
showFieldError(error.field, error.message); // ফর্ম ফিল্ডে হাইলাইট করা
} else if (error instanceof NetworkError) {
showToast(`সার্ভার এরর (${error.statusCode})`); // টোস্ট নোটিফিকেশন
} else {
console.error("অজানা এরর:", error); // যা প্রত্যাশিত ছিল না
}
}
}
এভাবে প্রতিটা এরর টাইপের জন্য আলাদা, উপযুক্ত UI প্রতিক্রিয়া দেখানো যায় — ভ্যালিডেশন এরর হলে ফর্ম ফিল্ডে লাল বর্ডার, নেটওয়ার্ক এরর হলে টোস্ট মেসেজ।
লাইভ ডেমো — বিভিন্ন এরর টাইপ আলাদাভাবে দেখানো
নিচের বাটনগুলো ইচ্ছাকৃতভাবে ভিন্ন ভিন্ন কাস্টম এরর থ্রো করে — লক্ষ্য করো প্রতিটার জন্য ভিন্ন মেসেজ দেখানো হচ্ছে।
async/await-এ এরর হ্যান্ডলিং
async ফাংশনে throw করা যেকোনো এরর স্বয়ংক্রিয়ভাবে একটা reject হওয়া Promise-এ পরিণত হয় — তাই try/catch দিয়েই সেটা ধরা যায়, আলাদা করে .catch() লাগে না।
async function fetchBook(id) {
const response = await fetch(`/api/books/${id}`);
if (!response.ok) {
throw new NetworkError("বই খুঁজে পাওয়া যায়নি", response.status);
}
return response.json();
}
async function displayBook(id) {
try {
const book = await fetchBook(id);
console.log(book);
} catch (error) {
// fetchBook-এর ভেতরের throw এখানে ধরা পড়ছে
console.log("বই লোড করা যায়নি:", error.message);
}
}
error.cause — এরর চেইনিং
আধুনিক JavaScript-এ একটা এরর অন্য একটা এররের কারণে হয়েছে সেটা cause দিয়ে ট্র্যাক করা যায় — এতে ডিবাগ করার সময় মূল কারণ খুঁজে পাওয়া সহজ হয়।
async function saveBookToLibrary(book) {
try {
await saveToDatabase(book);
} catch (originalError) {
throw new Error("বই সেভ করা যায়নি", { cause: originalError });
}
}
try {
await saveBookToLibrary(myBook);
} catch (error) {
console.log(error.message); // "বই সেভ করা যায়নি"
console.log(error.cause); // মূল database এরর, ডিবাগের জন্য দরকারি
}
একটা সম্পূর্ণ উদাহরণ — ফর্ম ভ্যালিডেশন সিস্টেম
চলো একাধিক কাস্টম এরর ক্লাস মিলিয়ে একটা বাস্তব ফর্ম ভ্যালিডেশন সিস্টেম বানাই।
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = "ValidationError";
this.field = field;
}
}
function validateBookForm(data) {
if (!data.title || data.title.trim() === "") {
throw new ValidationError("শিরোনাম আবশ্যক", "title");
}
if (!data.author || data.author.trim() === "") {
throw new ValidationError("লেখকের নাম আবশ্যক", "author");
}
if (data.pageCount && data.pageCount < 0) {
throw new ValidationError("পাতা সংখ্যা ঋণাত্মক হতে পারে না", "pageCount");
}
}
function handleFormSubmit(formData) {
try {
validateBookForm(formData);
console.log("✅ ভ্যালিডেশন সফল, সেভ করা হচ্ছে...");
// এখানে actual সেভ করার কোড থাকবে
} catch (error) {
if (error instanceof ValidationError) {
const inputEl = document.getElementById(error.field);
if (inputEl) {
inputEl.style.border = "2px solid red";
}
alert(error.message);
} else {
console.error("অপ্রত্যাশিত এরর:", error);
}
} finally {
console.log("ভ্যালিডেশন প্রক্রিয়া শেষ");
}
}
handleFormSubmit({ title: "", author: "আরিফ" });
// "শিরোনাম আবশ্যক" alert দেখাবে, title ইনপুট লাল বর্ডার পাবে
নিজে চেষ্টা করো
Exercise:
- একটা
AuthErrorকাস্টম ক্লাস বানাও (Error থেকে extend করে), যেটা লগইন ব্যর্থ হলে থ্রো হবে। - উপরের
validateBookFormফাংশনে আরও একটা চেক যোগ করো — author নামে সংখ্যা থাকলে এরর দাও। error.causeব্যবহার করে একটা এরর চেইন বানাও — যেমন "ফাইল আপলোড ব্যর্থ" এররের কারণ হিসেবে "নেটওয়ার্ক টাইমআউট" এরর দেখাও।- একটা ফাংশনে
finallyব্লক ব্যবহার করে টেস্ট করো এটা এরর হোক বা রিটার্ন হোক, দুই ক্ষেত্রেই রান হয় কিনা।
পরের পর্বে যা থাকছে
পরের পর্বে আমরা শিখব Module (import/export) — কীভাবে একটা বড় প্রজেক্টের কোড একাধিক ফাইলে ভাগ করে সংগঠিত রাখা যায়, আর সেগুলো একসাথে কীভাবে কাজ করে।