سلام دوستان. تنها برنامه‌ای که هیچ خطایی نداشته و نداره، برنامهٔ ننوشته‌شده هست. برنامه‌ای که توسط بزرگترین برنامه‌نویس‌ها نوشته شده هم در معرض خطا و اشتباه هست. برای همین توی هر زبانی ابزارهای فراوانی برای جلوگیری و مدیریت خطاها وجود داره. امروز می‌خوایم با یک ابزار درونی و ذاتی جاوااسکریپت برای مدیریت خطاهای برنامه آشنا بشیم.

امروز یاد می‌گیریم که:

 

توی جاوااسکریپت چه خطاهایی داریم؟ 🤔

ابتدا باید بدونیم که توی جاوااسکریپت کدهای ما توی ۲ مرحله پردازش میشه: ابتدا Parse-time و بعد Runtime.

مرحله Parse-time چیه؟

مرحله‌ای هست که جاوااسکریپت کدهایی که نوشتیم رو به زبان قابل فهم خودش ترجمه می‌کنه. توی این مرحله اجرای واقعی برنامهٔ ما هنوز اتفاق نمیوفته. بلکه همونطور که گفتیم فقط کدهای ما به یک حالت دیگه تبدیل میشه.

مرحله Runtime چیه؟

مرحله‌ای هست که کدهای برنامهٔ ما به طور واقعی اجرا و خروجی مد نظرمون به ما برگردونده میشه.

اگه توی کدهای ما قواعد نوشتاری زبان رعایت نشده باشه، کدهای ما برای جاوااسکریپت قابل فهم نیست و بنابراین جاوااسکریپت نمی‌تونه کدهای ما رو ترجمه کنه و در نتیجه برنامه متوقف میشه. به چنین خطاهایی که هنگام Parse-time رخ میده می‌گیم Syntax Error (خطاهایی که به نحوه نوشتن کد مربوط هست). برای مثال:

if (true) {

توی کد بالا براکت پایانی بدنه if حذف شده. این کد، یک کد غیر قابل فهم برای جاوااسکریپت هست. چنین خطاهایی باعث میشه کل اجرای برنامه متوقف بشه. Syntax Error ها رو می‌تونیم با نگاه کردن به ظاهر کد متوجه بشیم.

اما نوع دیگه‌ای از خطا هست که معمولاً تا زمانی که برنامهٔ ما به‌طور واقعی اجرا نشه نمی‌تونیم اونها رو تشخیص بدیم. به این خطاها می‌گیم خطاهای Runtime یا Exception. چون هنگام اجرای واقعی کد خودشون رو نشون میدن.

توی چنین شرایطی قواعد نوشتاری زبان جاوااسکریپت (پرانتزها، ویرگول‌ها، براکت‌ها و ...) به درستی رعایت شده و کدهای ما ظاهر معتبری داره و توسط جاوااسکریپت ترجمه میشه. اما موقع اجرا که می‌رسه: 💥 برنامه با خطا مواجه میشه. برای مثال:

function getUserFromServer() {
  return null;
}

const user = getUserFromServer();

alert(<<user.getName()>>);

تابع getUserFromServer برای مثال قراره اطلاعات کاربر رو از سرور بخونه و تحویل بده؛ اما فرضاً توی این کار ناموفق هست و null برمی‌گردونه. این کد ظاهر معتبری داره و به درستی توسط جاوااسکریپت ترجمه میشه. اما با اجرای اون می‌بینیم که خطا می‌گیریم که user is null.

 

همونطور که گفتیم خطاهای مربوط به Parse-time باعث میشن اجرای برنامه کلاً متوقف بشه و تا زمانی که خطاها رفع نشن برنامه قابل اجرا نیست. اما خطاهای Runtime توی یک برنامه بسیار رایج‌تر هستن. به این دلیل که این خطاها معمولاً مربوط به منطق یا ورودی‌های برنامه هست و اگه راهی برای مدیریت اونها نباشه، برنامه همیشه در معرض توقف و Crash کردن هست. حتی اگه برنامه متوقف بشه، ما دوست داریم پیغام مناسب‌تری به‌جای user is null به کاربر نشون بدیم. خوشبختانه راه‌هایی برای مدیریت چنین خطاهایی وجود داره که یکی از معروف‌ترین اونها دستور try...catch هست.

 

دستور try...catch چیه؟

اگه قطعه کدی داریم و احتمال می‌دیم که این قطعه کد باعث بروز خطا بشه، از دستور try...catch برای مهار کردن خطا و سپس انجام اقدام مناسب استفاده می‌کنیم.

ساختار کلی این دستور به صورت زیر هست:

try {
  // code
} catch (error) {
  // do something after error
}

ما کدهایی که احتمال می‌دیم خطا داشته باشن رو توی بدنهٔ try می‌نویسیم. توی بدنه catch کدهایی برای مدیریت خطا می‌نویسیم. اگه کدهایی که توی قسمت try می‌نویسیم دارای خطا باشه، کدهای این قسمت بلافاصله متوقف میشه و بعد کدهای قسمت catch پردازش میشه:

try {
  const user = null;
  user.getName();

  alert(); // ignored
  alert(); // ignored
  alert(); // ignored
} catch (error) {
>>   alert("Error!");
}

اما اگه کدهای قسمت try هیچ خطایی نداشته باشه، قسمت catch نادیده گرفته میشه:

try {
  alert("Done!");
} catch (error) {
  alert("Error!"); // ignored
}

دستور try...catch باعث میشه جریان برنامه متوقف نشه. یعنی حتی در صورت مواجه شدن با خطا، کدهایی که بعد از try...catch می‌نویسیم هم قابل اجرا هستن. مثال زیر رو در نظر بگیرید که بدون try...catch نوشته شده:

const user = null;
user.getName();

alert('Hello'); // ignored

همونطور که می‌بینیم به دلیل خطای مهارنشده توی خط ۲، برنامه متوقف شد و alert خط ۴ اجرا نشد. با استفاده از try...catch می‌تونیم خطا رو مهار کنیم تا جریان برنامه متوقف نشه:

try {
  const user = null;
  user.getName();
} catch (error) {
  // ...
}

alert('Hello'); // works!

با اجرای این کد همونطور که می‌بینیم با وجود خطا، alert خط آخر برای ما اجرا شد. اما باید بدونیم که کاربرد اصلی try...catch این نیست که مانع توقف برنامه بشه. کاربرد اصلی try...catch اینه که به‌جای اینکه جاوااسکریپت خطاهای ما رو مدیریت کنه، خودمون به‌صورت کدنویسی شده خطاها و جریان برنامه رو مدیریت و شخصی‌سازی کنیم.

 

متغیر error توی catch چیه؟

این متغیر، یک آبجکت و شامل اطلاعاتی درباره خطای رخ‌داده هست. توی این آبجکت دو پراپرتی مهم وجود داره. پراپرتی name که شامل نوع خطا و پراپرتی message شامل متن خطا هست:

try {
  const user = null;
  user.getName();
} catch (error) {
  alert(<<error.name>>);    // TypeError
  alert(<<error.message>>); // user is null
}

اسم این متغیر می‌تونه هر چیزی باشه که معمولاً e یا err هم در نظر گرفته میشه:

catch (e) { }
catch (err) { }

اگه به متغیر error احتیاجی نداریم، می‌تونیم از نوشتن (error) صرف نظر کنیم:

try {

} catch {

}

به این ویژگی گفته میشه Optional catch binding که جدیداً به جاوااسکریپت اضافه شده.

 

دستور try...catch...finally

فرض کنیم تابعی داریم به اسم clearCache که هدفش پاک کردن اطلاعات Cache شدهٔ برنامه هست. ما می‌خوایم تحت هر شرایطی (چه با خطا مواجه بشیم چه نشیم) این تابع رو اجرا کنیم. کد زیر رو در نظر بگیرید:

try {
  // code
  clearCache();
} catch {
  // code
  clearCache();
}

همونطور که می‌بینیم مجبور هستیم توی هر دو قسمت try و catch از این تابع استفاده کنیم. معمولاً نوشتن کدهای تکراری کار جالبی نیست. باید راهی باشه که بتونیم یک سری کدهای خاص رو تحت هر شرایطی اجرا کنیم. برای این هم یک راهکار وجود داره و اون هم استفاده از دستور finally هست:

try {
  const x = 10;
  x = 20; // error
} catch {
  // code
} finally {
  alert("I'm executed anyway!");
}

کدی که توی بلاک finally می‌نویسیم، بعد از به پایان رسیدن کار try اجرا میشه؛ بدون توجه به اینکه try خطا داره یا نه.

 

دستور try...finally

همونطور که احتمالاً حدس زدید، توی این دستور دیگه از catch استفاده نمی‌کنیم چون مدیریت خطا برامون مهم نیست:

try {
  const x = 10;
  x = 20; // error

  alert(); // ingored
  alert(); // ingored
} finally {
  <<alert("I learn JS anyway.")>>;
}

alert("Normal code"); // ignored

نکات مهمی که باید در نظر داشته باشیم، توی چنین کدی اگه try خطا داشته باشه:

  1. ادامهٔ اجرای try متوقف میشه
  2. کدهای توی finally اجرا میشه
  3. خطایی که توی try به وجود اومده مثل حالت خطاهای مهارنشده بروز داده میشه و باعث توقف برنامه میشه

 

 

چه زمانی از try...catch استفاده کنیم؟ 🤔

توی هر برنامه‌ای، استفاده بی‌رویه از هر دستوری باعث پیچیدگی و افت کیفیت کدها میشه. احتمالاً اگه هنوز اول راه یادگیری هستین و تجربه زیادی توی کدنویسی ندارید، شاید اصلاً نیازی به استفاده از این دستورها احساس نکنین. باید بدونیم که به غیر از بعضی شرایط خاص، نیازی نیست که همه کدها رو ببریم توی try...catch.

ما زمانی از try...catch استفاده می‌کنیم که هدف مشخصی برای زمانی که خطا رخ داد درنظر گرفتیم و دقیقاً می‌دونیم می‌خوایم چه‌کاری انجام بدیم. خیلی از شرایطی که توی برنامه‌ها وجود داره رو می‌تونیم با if/else ساده بررسی و مدیریت کنیم. مثالی که سرتاسر این آموزش بررسی کردیم رو به یاد بیاریم:

try {
  const user = getUserFromServer();
  user.getName();
} catch (error) {
  alert("Invalid user");
}

برای کدی به این سادگی (که به هدف سادگی برای آموزش انتخاب شده) نیاز به try...catch نیست و اون رو می‌تونیم به این صورت بنویسیم:

const user = getUserFromServer();

if (user !== null) {
  user.getName();
} else {
  alert("Invalid user");
}

 

خب دوستان یاد گرفتیم که چطوری با try...catch می‌تونیم خطاهای برنامه رو مدیریت کنیم. باید بدونیم که اهمیت این دستورها زمانی مشخص میشه که با برنامه‌های بزرگ سر و کار داریم. روزتون خوش 😉✌️

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch

https://javascript.info/try-catch

https://stackoverflow.com/questions/2737328/why-should-i-not-wrap-every-block-in-try-catch

https://www.freecodecamp.org/news/try-catch-in-javascript/

https://stackoverflow.com/questions/1722964/when-to-use-try-catch-blocks