آموزش دستور try…catch…finally در جاوا اسکریپت
هرچقدر در برنامهنویسی عالی باشیم، گاهی اوقات اسکریپتهای ما دارای خطا هستند. این خطاها ممکن است به دلیل اشتباهات ما، ورودی غیرمنتظره کاربر، پاسخ اشتباه سرور و هزاران دلیل دیگر رخ دهند. معمولاً یک اسکریپت در صورت بروز خطا متوقف میشود یا به حالت dies میرود و آن را در کنسول چاپ میکند؛ اما یک ساختار نحوی try…catch در جاوا اسکریپت وجود دارد که به ما امکان میدهد خطاها را « catch » یا نادیده بگیریم تا اسکریپت بتواند بهجای توقف، کار معقولتری انجام دهد. در این پست آموزشی از مکتب خونه میخواهیم در رابطه با آموزش دستور try…catch…finally در جاوا اسکریپت باهم به گفتگو بپردازیم. پس باما همراه باشید.
سینتکس دستور try…catch در جاوا اسکریپت
ساختار try…catch در جاوا اسکریپت دو بلوک اصلی دارد، try و سپس catch. به صورت زیر:
try {
// code...
} catch (err) {
// error handling
}
نحوه کار دستور try…catch…finally
نحوه کار دستور try…catch…finally در جاوا اسکریپت به صورت زیر است:
- ابتدا کد موجود در try اجرا میشود.
- اگر خطایی وجود نداشته باشد، catch (err) نادیده گرفته میشود: اجرا به پایان تلاش میرسد و ادامه مییابد و از catch میگذرد.
- اگر خطایی رخ دهد، اجرای try متوقف میشود و کنترل به ابتدای catch (err) جریان مییابد. متغیر err (میتوانیم از هر نامی برای آن استفاده کنیم) حاوی یک شیء خطا با جزئیات مربوط به اتفاقی بوده که رخداده است.
فلوچارت زیر نحوه اجرای دستور try…catch…finally را در جاوا اسکریپت بیان میکند.
بنابراین، یک خطا در بلوک try اسکریپت را به حالت متوقف نمیبرد و ما در جاوا اسکریپت این شانس را داریم که آن را بهطور کامل مدیریت کنیم.
پیشنهاد مطالعه: آموزش متد Includes در جاوا اسکریپت
مثال از try…catch…finally در جاوا اسکریپت
یک مثال بدون خطا: کد زیر هشدار (1) و (2) را نشان میدهد و این کد بدون خطا است:
try {
alert('Start of try runs'); // (1)
// ...no errors here
alert('End of try runs'); // (2)
} catch (err) {
alert('Catch is ignored, because there are no errors'); // (3)
}
یک مثال با خطا: (1) و (3) را بهعنوان ارور نشان میدهد:
try {
alert('Start of try runs'); // (1)
lalala; // error, variable is not defined!
alert('End of try (never reached)'); // (2)
} catch (err) {
alert(`Error has occurred!`); // (3)
}
ساختار دستور try…catch…finally در زمان اجرا
برای قابلاستفاده بودن دستور try…catch…finally در جاوا اسکریپت کد باید قابلاجرا باشد. بهعبارتدیگر، باید جاوا اسکریپت معتبر باشد.
اگر کد ازنظر نحوی اشتباه باشد، کار نمیکند، بهعنوانمثال کد زیر دارای براکت های بی همتا است:
try {
{{{{{{{{{{{{
} catch (err) {
alert("The engine can't understand this code, it's invalid");
}
موتور جاوا اسکریپت ابتدا کد را میخواند و سپس آن را اجرا میکند. خطاهایی که در مرحله خواندن رخ میدهد، خطاهای «تجزیه زمان» نامیده میشوند و غیرقابلجبران هستند، این به این دلیل است که موتور نمیتواند کد را درک کند.
بنابراین، try…catch…finally در جاوا اسکریپت فقط میتواند خطاهایی را که در کدهای معتبر رخ میدهد، کنترل کند. چنین خطاهایی خطاهای زمان اجرا یا گاهی اوقات، استثنا نامیده میشوند.
پیشنهاد مطالعه: Scope در جاوا اسکریپت چیست؟ آموزش اسکوپ به زبان ساده
دستور try…catch…finally در جاوا اسکریپت برای همزمانی
اگر یک استثنا در کد «زمانبندیشده» اتفاق بیفتد، مانند setTimeout، دستور try…catch…finally در جاوا اسکریپت آن را نمیگیرد:
try {
setTimeout(function() {
noSuchVariable; // script will die here
}, 1000);
} catch (err) {
alert( "won't work" );
}
این به این دلیل است که خود تابع بعداً اجرا میشود، زمانی که موتور قبلاً از ساختار try…catch خارجشده است. برای گرفتن یک استثنا در داخل یک تابع زمانبندیشده، try…catch…finally باید در داخل آن تابع باشد:
setTimeout(function() {
try {
noSuchVariable; // try...catch handles the error!
} catch {
alert( "error is caught here!« );
}
}, 1000);
شیء خطا
هنگامیکه یک خطا رخ میدهد، جاوا اسکریپت یک شیء تولید میکند که حاوی جزئیات مربوط به آن است. سپس شیء بهعنوان آرگومان برای گرفتن ارسال میشود. به مثال زیر دقت کنید.
try {
// ...
} catch (err) { // the "error object", could use another word instead of err
// ...
}
برای تمام خطاهای داخلی، شیء خطا دو ویژگی اصلی دارد:
- نام خطا. بهعنوانمثال، برای یک متغیر تعریفنشده که ReferenceError است.
- پیام متنی در مورد جزئیات خطا
ویژگیهای غیراستاندارد دیگری نیز در اکثر محیطها وجود دارد. یکی از پرکاربردترین و پشتیبانی شده ترین موارد در رابطه با پشته است:
پشته تماس فعلی: رشتهای با اطلاعاتی در مورد توالی تماسهای تودرتو که منجر به خطا شده است که برای اهداف اشکالزدایی استفاده میشود. برای مثال:
try {
lalala; // error, variable is not defined!
} catch (err) {
alert(err.name); // ReferenceError
alert(err.message); // lalala is not defined
alert(err.stack); // ReferenceError: lalala is not defined at (...call stack)
// Can also show an error as a whole
// The error is converted to string as "name: message"
alert(err); // ReferenceError: lalala is not defined
}
استفاده از دستور try… catch
در این بخش به یک مثال عملی از try…catch…finally در جاوا اسکریپت میپردازیم تا بهخوبی نحوه استفاده از آن برای ما تفهیم شود.
همانطور که میدانیم، جاوا اسکریپت از متد JSON.parse(str) برای خواندن مقادیر کدگذاری شده با JSON پشتیبانی میکند. معمولاً برای رمزگشایی دادههای دریافتی از طریق شبکه، از سرور یا منبع دیگری استفاده میشود. ما آن را دریافت میکنیم و JSON.parse را به این صورت فراخوانی میکنیم:
let json = '{"name":"John", "age": 30}'; // data from the server
let user = JSON.parse(json); // convert the text representation to JS object
// now user is an object with properties from the string
alert( user.name ); // John
alert( user.age ); // 30
اگر json نادرست باشد، JSON.parse خطایی ایجاد میکند، بنابراین اسکریپت به حالت توقف میرود. بهاینترتیب، اگر مشکلی در دادهها وجود داشته باشد، بازدیدکننده هرگز آن را نمیداند (مگر اینکه کنسول توسعهدهنده را باز کند و ببینند).
حال بیایید از دستور try…catch در جاوا اسکریپت برای رسیدگی به این خطا استفاده کنیم:
let json = "{ bad json }";
try {
let user = JSON.parse(json); // when an error occurs...
alert( user.name ); // doesn't work
} catch (err) {
// ...the execution jumps here
alert( "Our apologies, the data has errors, we'll try to request it one more time.« );
alert( err.name );
alert( err.message );
}
در اینجا ما از بلوک catch فقط برای نشان دادن پیام استفاده میکنیم، اما میتوانیم کارهای بیشتری انجام دهیم: ارسال یک درخواست شبکه جدید، پیشنهاد جایگزینی برای بازدیدکننده، ارسال اطلاعات مربوط به خطا به یک مرکز گزارشگیری، …. همهچیز خیلی بهتر از مردن است.
پیشنهاد مطالعه: آموزش تابع Date در جاوا اسکریپت به زبان ساده + مثال و نمونه کد
پرتاب اشتباهات خودمان
اگر json ازنظر نحوی صحیح باشد، اما ویژگی نام لازم را نداشته باشد، چه؟ مثل این:
let json = '{ "age": 30 }'; // incomplete data
try {
let user = JSON.parse(json); // no errors
alert( user.name ); // no name!
} catch (err) {
alert( "doesn't execute" );
}
در اینجا JSON.parse بهطورمعمول اجرا میشود، اما عدم وجود نام درواقع برای ما یک خطا است. برای مدیریت خطا در این صورت از عملگر throw استفاده میکنیم.
اپراتور throw یا عملگر پرتاب
عملگر throw در جاوا اسکریپت یک خطا ایجاد میکند. سینتکس آن بهصورت زیر است:
throw <error object></error>
ازنظر فنی، ما میتوانیم از هر چیزی بهعنوان یک شیء خطا استفاده کنیم.
جاوا اسکریپت سازندههای داخلی زیادی برای خطاهای استاندارد دارد: Error، SyntaxError، ReferenceError، TypeError و غیره. ما میتوانیم از آنها برای ایجاد اشیاء خطا نیز استفاده کنیم. سینتکس آنها به این صورت است:
let error = new Error(message);
// or
let error = new SyntaxError(message);
let error = new ReferenceError(message);
// ...
برای خطاهای داخلی ویژگی name دقیقاً نام سازنده است؛ و پیام از argument گرفتهشده است. برای مثال:
let error = new Error("Things happen o_O");
alert(error.name); // Error
alert(error.message); // Things happen o_O
حال بیایید ببینیم JSON.parse چه نوع خطایی ایجاد میکند:
try {
JSON.parse("{ bad json o_O }");
} catch (err) {
alert(err.name); // SyntaxError
alert(err.message); // Unexpected token b in JSON at position 2
}
همانطور که میبینیم، این یک SyntaxError است. در این مثال عدم وجود نام یک خطا است، زیرا کاربران باید یک نام داشته باشند. پس بیایید آن را پرتاب کنیم:
let json = '{ "age": 30 }'; // incomplete data
try {
let user = JSON.parse(json); // no errors
if (!user.name) {
throw new SyntaxError("Incomplete data: no name"); // (*)
}
alert( user.name );
} catch (err) {
alert( "JSON Error: " + err.message ); // JSON Error: Incomplete data: no name
}
در خط (*)، عملگر throw با پیام دادهشده یک SyntaxError ایجاد میکند، به همان روشی که جاوا اسکریپت خود آن را ایجاد میکند. اجرای try فوراً متوقف میشود و جریان کنترل به حالت گیر در میآید. اکنون catch به یک مکان واحد برای رسیدگی به همه خطاها تبدیل شد.
باز انداختن در دستور try…catch…finally
در مثال بالا از دستور try…catch برای رسیدگی به دادههای نادرست استفاده میکنیم؛ اما آیا ممکن است خطای غیرمنتظره دیگری در بلوک try رخ دهد؟ مثلاً یک خطای برنامهنویسی (متغیر تعریفنشده است) یا چیز دیگری. مثلاً:
et json = '{ "age": 30 }'; // incomplete data
try {
user = JSON.parse(json); // forgot to put "let" before user
// ...
} catch (err) {
alert("JSON Error: " + err); // JSON Error: ReferenceError: user is not defined
// (no JSON Error actually)
}
در این مقاله، try…catch برای دریافت خطاهای “دادههای نادرست” قرار دادهشده است اما ممکن است هر خطای دیگری نیز رخ دهد. به دلیل ماهیت خود، catch همه خطاها را از طریق try دریافت میکند. در اینجا یک خطای غیرمنتظره دریافت میکند، اما همچنان همان پیام “خطای JSON” را نشان میدهد. این اشتباه است و همچنین اشکالزدایی کد را دشوارتر میکند.
- برای جلوگیری از چنین مشکلاتی، میتوانیم از تکنیک rethrow در جاوا اسکریپت استفاده کنیم. یک قانون ساده برای آن وجود دارد:
- Catch باید فقط خطاهایی را که میداند پردازش کند و بقیه خطاها را rethrow بازگردانی کند.
تکنیک rethrow را میتوان با جزئیات بیشتر به شرح زیر توضیح داد:
- Catch همه خطاها را دریافت میکند.
- در بلوک catch (err) شیء خطا err را تحلیل میکنیم.
معمولاً میتوانیم نوع خطا را با استفاده از عملگر instanceof بررسی کنیم:
همچنین میتوانیم نام کلاس خطا را از ویژگی err.name دریافت کنیم. همه خطاهای بومی آن را دارند. گزینه دیگر خواندن err.constructor.name است.
در کد زیر، از rethrowing استفاده میکنیم تا catch فقط SyntaxError را کنترل کند:
let json = '{ "age": 30 }'; // incomplete data
try {
let user = JSON.parse(json);
if (!user.name) {
throw new SyntaxError("Incomplete data: no name");
}
blabla(); // unexpected error
alert( user.name );
} catch (err) {
if (err instanceof SyntaxError) {
alert( "JSON Error: " + err.message );
} else {
throw err; // rethrow (*)
}
}
خطای پرتاب روی خط (*) از داخل بلوک catch از دستور try…catch خارج میشود و میتواند توسط یک ساختار try…catch بیرونی (در صورت وجود) شناسایی شود یا اسکریپت را از بین ببرد.
بنابراین بلوک catch درواقع فقط خطاهایی را کنترل میکند که میداند چگونه با آنها برخورد کند و سایر خطاها را نادیده میگیرد. مثال زیر نشان میدهد که چگونه چنین خطاهایی را میتوان با یک سطح دیگر از دستور try…catch در جاوا اسکریپت گرفت:
function readData() {
let json = '{ "age": 30 }';
try {
// ...
blabla(); // error!
} catch (err) {
// ...
if (!(err instanceof SyntaxError)) {
throw err; // rethrow (don't know how to deal with it)
}
}
}
try {
readData();
} catch (err) {
alert( "External catch got: " + err ); // caught it!
}
در اینجا readData فقط میداند که چگونه SyntaxError را مدیریت کند، درحالیکه دستور try…catch در جاوا اسکریپت میداند که چگونه همهچیز را مدیریت کند.
دستور try…catch…finally در جاوا اسکریپت
ساختار دستور try…catch یک بند کد دیگر دارد و آن هم بند finally است. اگر وجود داشته باشد، در همه موارد اجرا میشود:
- پس از try، اگر خطایی وجود نداشت
- پس از catch، در صورت وجود خطا.
سینتکس دستور try…catch…finally در جاوا اسکریپت بهصورت زیر است.
try {
... try to execute the code ...
} catch (err) {
... handle errors ...
} finally {
... execute always ...
}
یک نمونه کد دیگر از دستور try…catch…finally در جاوا اسکریپت:
try {
alert( 'try' );
if (confirm('Make an error?')) BAD_CODE();
} catch (err) {
alert( 'catch' );
} finally {
alert( 'finally' );
}
این کد دو راه برای اجرا دارد:
- اگر ارور وجود داشته باشد، اجرای دستور try…catch…finally انجم میپذیرد.
- اگر خطا وجود نداشته باشد، try -> finally اجرا میشود.
بند آخر اغلب زمانی استفاده میشود که ما شروع به انجام کاری میکنیم و میخواهیم آن را درهرصورت از آن خروجی بگیریم.
بهعنوانمثال، ما میخواهیم مدتزمانی را که تابع اعداد فیبوناچی fib(n) طول میکشد را اندازهگیری کنیم. طبیعتاً میتوانیم اندازهگیری را قبل از اجرا شروع کنیم و بعدازآن تمام کنیم؛ اما اگر در حین فراخوانی تابع خطایی رخ دهد، چه؟ بهطور خاص، پیادهسازی fib(n) در کد زیر یک خطا برای اعداد منفی یا غیر صحیح برمیگرداند.
بند آخر مکانی عالی برای پایان دادن به اندازهگیریها بدون توجه به هر چیزی است. درنهایت در اینجا تضمین میکند که زمان در هر دو موقعیت بهدرستی اندازهگیری میشود، در صورت اجرای موفقیتآمیز fib و در صورت بروز خطا در آن:
میتوانید با اجرای کد با واردکردن عدد 35 در اعلان، آن را بررسی کنید. بهطورمعمول و درنهایت پس از امتحان اجرا میشود. همچنین اگر عدد 1- را وارد کنید خطای فوری رخ میدهد و اجرا 0 میلیثانیه طول میکشد. هر دو اندازهگیری بهدرستی انجام میشود.
پیشنهاد مطالعه: آموزش Object در جاوا اسکریپت به زبان ساده + مثال عملی
finally and return
بند finally برای هر خروجی از دستور try…catch به کار میرود که شامل بازگشت صریح میشود. در مثال زیر، یک return در try وجود دارد. در این حالت، درنهایت درست قبل از بازگشت کنترل به کد بیرونی اجرا میشود.
function func() {
try {
return 1;
} catch (err) {
/* ... */
} finally {
alert( 'finally' );
}
}
alert( func() ); // first works alert from finally, and then this one
دستور try…finally در جاوا اسکریپت
ساختار try…finally، بدون بند catch نیز میتواند کاربرد داشته باشد. زمانی که نمیخواهیم خطاها را در اینجا مدیریت کنیم و آن را اعمال میکنیم، اما میخواهیم مطمئن باشیم که فرآیندهایی که شروع کردهایم خروجی میدهند از دستور try…finally استفاده میکنیم.
function func() {
// start doing something that needs completion (like measurements)
try {
// ...
} finally {
// complete that thing even if all dies
}
}
کلام پایانی
ساختار دستور try…catch در جاوا اسکریپت امکان رسیدگی به خطاهای زمان اجرا را فراهم میکند. این به معنای واقعی کلمه اجازه میدهد تا کد را امتحان کنیم و خطاهای موجود در آن را بیابیم.
سینتکس دستور try…catch…finally در جاوا اسکریپت بهصورت زیر است:
try {
// run this code
} catch (err) {
// if an error happened, then jump here
// err is the error object
} finally {
// do in any case after try/catch
}
ممکن است بخش catch وجود نداشته باشد یا بخش finally وجود نداشته باشد. در این صورت دستور try…catch و دستور try…finally کاربرد خواهند داشت.
آموزش جاوا اسکریپت با مکتب خونه
بخش بزرگی از آموزشهای مکتب خونه را دورههای برنامهنویسی تشکیل میدهند. جاوا اسکریپت یک زبان برنامهنویسی برای فرانتاند است که در سالهای اخیر قابلیت برنامهنویسی برای بکاند را نیز پشتیبانی میکند؛ با یادگیری این زبان میتوانید به عنوان برنامهنویس فولاستک فعالیت کنید. پیشنهاد میکنیم برای یادگیری این مهارت حتماً از دوره های آموزش جاوا اسکریپت و آموزش برنامه نویسی مکتب خونه بازدید فرمایید.