مهم نیست که چقدر در برنامهنویسی عالی هستیم، گاهی اوقات اسکریپتهای ما ارورهایی (error) دارند. این ارورها ممکن است به دلیل اشتباهات ما، ورودی غیر منتظره کاربر، پاسخ نادرست سرور و هزاران دلیل دیگر رخ بدهند.
معمولا، هنگامی که اروری رخ میدهد اسکریپت میمیرد (بلافاصله متوقف میشود) و آن را در کنسول چاپ میکند.
اما یک ساختار سینتکسی try...catch وجود دارد که به ما این امکان را میدهد که ارورها را «بگیریم (catch)» تا اسکریپت، به جای مردن، بتواند کاری منطقیتر انجام دهد.
سینتکس “try…catch”
ساختار try...catch دو بلوک اصلی دارد: try و سپس catch:
try {
// ...کد
} catch (err) {
// مدیریت ارور
}
این سینتکس اینگونه کار میکند:
- ابتدا، کد درون
try {...}اجرا میشود. - اگر اروری وجود نداشت، سپس
catch (err)نادیده گرفته میشود: اجرای برنامه به انتهایtryمیرسد و با گذشتن ازcatchادامه مییابد. - اگر اروری رخ دهد، سپس اجرای
tryمتوقف شده و کنترل برنامه به ابتدایcatch (err)میرود. متغیرerr(میتوانیم هر نامی برای آن استفاده کنیم) شامل شیء اروری حاوی جزئیاتی درباره چیزی که اتفاق افتاده است.
پس یک ارور درون بلوک try {...} اسکریپت را نمیکشد – ما شانسی برای مدیریت آن درون catch داریم.
بیایید به چند مثال نگاهی بیاندازیم.
-
یک مثال بدون ارور:
alertخطوط(1)و(2)را نشان میدهد:try { alert('ابتدای try اجرا میشود'); // (1) <-- // اروری اینجا وجود ندارد... alert('انتهای try اجرا میشود'); // (2) <-- } catch (err) { alert('نادیده گرفته میشود چون اروری وجود ندارد Catch'); // (3) } -
مثالی شامل یک ارور: خطوط
(1)و(3)را نمایش میدهد:try { alert('ابتدای try اجرا میشود'); // (1) <-- lalala; // !ارور، متغیر تعریف نشده است alert('(هیچ گاه به اینجا نمیرسد) try انتهای'); // (2) } catch (err) { alert(`ارور رخ داد!`); // (3) <-- }
try...catch فقط برای ارورهای هنگام اجرای برنامه کار میکندبرای اینکه try...catch کار کند، کد باید قابل اجرا باشد. به عبارتی دیگر، باید کد جاوااسکریپت معتبر باشد.
اگر کد از لحاظ سینتکسی غلط باشد کار نمیکند، برای مثال اگر آکولادهای بیهمتا داشته باشد:
try {
{{{{{{{{{{{{
} catch (err) {
alert("موتور جاوااسکریپت نمیتواند این کد را متوجه شود. این کد نامعتبر است.");
}
موتور جاوااسکریپت ابتدا کد را میخواند و سپس آن را اجرا میکند. ارورهایی که در فاز خواندن رخ میدهند، ارورهای «زمان تجزیه (parse-time errors)» نامیده میشوند و قابل پوشش نیستند (از درون همان کد). به این دلیل که موتور نمیتواند کد را متوجه شود.
پس try...catch تنها میتواند ارورهایی که در کد معتبر رخ میدهند را مدیریت کند. چنین ارورهایی «ارورهای هنگام اجرا (runtime errors)» یا گاهی اوقات «استثناها (exceptions)» نامیده میشوند.
try...catch به صورت همگام کار میکنداگر یک استثناء در کدی «برنامهریزی شده» رخ دهد، مثلا در setTimeout، سپس try...catch آن را نمیگیرد:
try {
setTimeout(function() {
noSuchVariable; // اسکریپت اینجا میمیرد
}, 1000);
} catch (err) {
alert( "کار نخواهد کرد" );
}
به این دلیل که خود تابع بعدا اجرا میشود، زمانی که موتور ساختار try...catch را پشت سر گذاشته است.
برای اینکه استثناء را درون یک تابع برنامهریزی شده بگیریم، try...catch باید درون آن تابع باشد:
setTimeout(function() {
try {
noSuchVariable; // !ارور را مدیریت میکند try...catch
} catch {
alert( "ارور اینجا گرفته میشود!" );
}
}, 1000);
شیء Error
زمانی که یک ارور رخ میدهد، جاوااسکریپت شیءای حاوی جزئیاتی درباره آن را ایجاد میکند. این شیء به عنوان آرگومان به catch پاس داده میشود:
try {
// ...
} catch (err) { // <-- استفاده کنیم err این «شیء ارور» است، میتوانستیم از کلمهای دیگر به جای
// ...
}
برای تمام ارورهای درونساخت، شیء ارور دو ویژگی اصلی دارد:
name- اسم ارور. برای مثال، برای یک متغیر تعریف نشده برابر با
"ReferenceError"است. message- پیام متنی درباره جزئیات ارور.
در اکثر محیطها ویژگیهای غیر استاندارد دیگر هم وجود دارد. یکی از ویژگیهایی که به طور گسترده استفاده و پشتیبانی میشود:
stack- پشته فراخوانی کنونی: رشتهای حاوی اطلاعاتی درباره دنباله فراخوانیهایی که موجب رخ دادن ارور شدند. برای اهداف اشکالزدایی استفاده میشود.
برای مثال:
try {
lalala; // !ارور، متغیر تعریف نشده است
} catch (err) {
alert(err.name); // ReferenceError
alert(err.message); // lalala is not defined
alert(err.stack); // ReferenceError: lalala is not defined at (...پشته فراخوانیها)
// میتوانستیم ارور را به طور کامل هم نشان دهیم
// به رشته تبدیل میشود «name: message» ارور به صورت
alert(err); // ReferenceError: lalala is not defined
}
پیوند اختیاری «catch»
اگر ما به جزئیات ارور نیازی نداریم، catch میتواند آن را حذف کند:
try {
// ...
} catch { // <-- (err) بدون
// ...
}
استفاده از “try…catch”
بیایید یک مورد استفاده از try...catch را در دنیای واقعی ببینیم.
همانطور که از قبل میدانیم، جاوااسکریپت از متد JSON.parse(str) برای خواندن مقدارهایی که به صورت جیسان کدگذاری شدهاند پشتیبانی میکند.
این متد معمولا برای کدبرداری دادهای که از طریق شبکه دریافت شده است، از سرور یا منبعی دیگر، استفاده میشود.
ما داده را دریافت میکنیم و JSON.parse را اینگونه فراخوانی میکنیم:
let json = '{"name":"John", "age": 30}'; // داده دریافت شده از سرور
let user = JSON.parse(json); // تبدیل نمایش متنی به شیء جاوااسکریپت
// شیءای حاوی ویژگیهای دریافت شده از رشته است user حالا
alert( user.name ); // John
alert( user.age ); // 30
شما میتوانید در فصل متدهای JSON، toJSON اطلاعاتی با جزئیات بیشتر درباره جیسان پیدا کنید.
اگر json شکل درستی نداشته باشد، JSON.parse یک ارور ایجاد میکند، پس اسکریپت «میمیرد».
آیا ما باید به آن راضی باشیم؟ قطعا نه!
اینگونه، اگر داده مشکلی داشته باشد، بازدید کننده هرگز آن را نخواهد دانست (مگر اینکه آنها کنسول توسعهدهنده را باز کنند). و مردم چیزی که بدون پیام اروری «میمیرد» را دوست ندارند.
بیایید از try...catch برای مدیریت ارور استفاده کنیم:
let json = "{ bad json }";
try {
let user = JSON.parse(json); // <-- ...زمانی که اروری رخ میدهد
alert( user.name ); // کار نمیکند
} catch (err) {
// اجرای برنامه به اینجا میپرد...
alert( "پوزش میخواهیم، داده دارای ارور است، ما سعی خواهیم کرد یک بار دیگر برای آن درخواست کنیم." );
alert( err.name );
alert( err.message );
}
اینجا ما از بلوک catch فقط برای نمایش پیام استفاده میکنیم، اما میتوانیم کارهای بیشتری انجام دهیم: یک درخواست شبکه جدید ارسال کنیم، یک راه جایگزین به بازدیدکننده پیشنهاد کنیم، اطلاعاتی درباره ارور را به logging facility ارسال کنیم و… . هر چیزی از مردن بهتر است.
پرتاب ارورهای خودمان
اگر json از لحاظ سینتکس درست باشد اما ویژگی مورد نیاز name را نداشته باشد چه؟
مثل اینجا:
let json = '{ "age": 30 }'; // داده ناقض
try {
let user = JSON.parse(json); // <-- اروری وجود ندارد
alert( user.name ); // !وجود ندارد name ویژگی
} catch (err) {
alert( "اجرا نمیشود" );
}
اینجا JSON.parse به صورت طبیعی اجرا میشود اما در واقع نبودن name برای ما یک ارور است.
برای یکی کردن مدیریت ارور، ما از عملگر throw استفاده میکنیم.
عملگر «Throw»
عملگر