| الكلمة المفتاحية | الوظيفة |
|---|---|
| try | كتلة الكود المُحتمل فشله |
| catch(error) | التقاط الخطأ ومعالجته |
| finally | ينفَّذ دائماً بغض النظر عن النتيجة |
| throw | رمي خطأ مخصص يدوياً |
معالجة الأخطاء (Error Handling) بشكل صحيح تُميّز الكود الاحترافي عن الكود الهش الذي ينهار عند أي مشكلة غير متوقعة. try...catch هو البناء الأساسي لذلك: تضع الكود الذي قد يفشل داخل كتلة try، وإذا حدث أي خطأ (Exception) أثناء تنفيذه، يتوقف تنفيذ باقي كتلة try فوراً وينتقل التحكم مباشرة لكتلة catch مع تمرير كائن الخطأ إليها.
يمكن إضافة كتلة finally اختيارية بعد catch، وتُنفَّذ دائماً بغض النظر تماماً عن حدوث خطأ أو عدمه — مثالية لتنظيف الموارد (مثل إخفاء مؤشر تحميل أو إغلاق اتصال) الذي يجب أن يحدث في كل الأحوال. يمكنك أيضاً رمي أخطائك المخصصة يدوياً باستخدام throw، ويمكن رمي أي قيمة نظرياً، لكن الممارسة الصحيحة دائماً هي رمي كائن من نوع Error (أو فئة ترثه) لأنه يحمل خصائص قياسية مفيدة مثل message وname وstack.
يمكن إنشاء أنواع أخطاء مخصصة عبر إنشاء فئة ترث من Error المدمجة، مما يسمح بتصنيف الأخطاء بدقة أكبر (مثل ValidationError أو NetworkError) والتعامل معها بشكل مختلف حسب نوعها باستخدام instanceof داخل كتلة catch — هذا مفيد جداً في التطبيقات الكبيرة حيث تحتاج استجابات مختلفة لأنواع فشل مختلفة.
مع الأكواد غير المتزامنة تحديداً، من الضروري تذكّر أن try...catch حول await يلتقط فشل ذلك الـ Promise المحدد فقط، بينما استخدام .catch() مع Promises التقليدية (بدون async/await) يلتقط أي رفض في سلسلة then() الكاملة السابقة له. أيضاً من المهم عدم "ابتلاع" الأخطاء بصمت (كتابة catch فارغة دون أي إجراء)، لأن هذا يُخفي مشاكل حقيقية ويجعل تصحيح الأخطاء لاحقاً كابوساً حقيقياً.
| 1 | try { |
| 2 | const نتيجة = عملية_قد_تفشل(); |
| 3 | console.log(نتيجة); |
| 4 | } catch (خطأ) { |
| 5 | console.error("حدث خطأ:", خطأ.message); |
| 6 | } finally { |
| 7 | console.log("ينفَّذ دائماً بغض النظر عن النتيجة"); |
| 8 | } |
| 9 | |
| 10 | // رمي خطأ مخصص |
| 11 | if (عمر < 0) { |
| 12 | throw new Error("العمر لا يمكن أن يكون سالباً"); |
| 13 | } |
مثال 1: فئة خطأ مخصصة والتمييز بينها وبين أخطاء أخرى
| 1 | // فئة خطأ مخصصة للتمييز بين أنواع الأخطاء |
| 2 | class خطأ_التحقق extends Error { |
| 3 | constructor(رسالة) { |
| 4 | super(رسالة); |
| 5 | this.name = "خطأ_التحقق"; |
| 6 | } |
| 7 | } |
| 8 | |
| 9 | function تسجيل_مستخدم(بريد) { |
| 10 | if (!بريد.includes("@")) { |
| 11 | throw new خطأ_التحقق("صيغة البريد الإلكتروني غير صحيحة"); |
| 12 | } |
| 13 | return "تم التسجيل بنجاح"; |
| 14 | } |
| 15 | |
| 16 | try { |
| 17 | تسجيل_مستخدم("بريد_خاطئ"); |
| 18 | } catch (خطأ) { |
| 19 | if (خطأ instanceof خطأ_التحقق) { |
| 20 | console.log("خطأ في التحقق:", خطأ.message); |
| 21 | } else { |
| 22 | console.log("خطأ غير متوقع:", خطأ.message); |
| 23 | } |
| 24 | } |
مثال 2: معالجة شاملة لأخطاء الشبكة المختلفة
| 1 | // معالجة أخطاء الشبكة بشكل شامل واحترافي مع async/await |
| 2 | async function جلب_بأمان(رابط) { |
| 3 | try { |
| 4 | const استجابة = await fetch(رابط); |
| 5 | if (!استجابة.ok) { |
| 6 | throw new Error(`فشل الخادم برمز: ${استجابة.status}`); |
| 7 | } |
| 8 | return await استجابة.json(); |
| 9 | } catch (خطأ) { |
| 10 | if (خطأ instanceof TypeError) { |
| 11 | console.error("فشل الاتصال بالشبكة تماماً — تحقق من الإنترنت"); |
| 12 | } else { |
| 13 | console.error("خطأ في جلب البيانات:", خطأ.message); |
| 14 | } |
| 15 | return null; // قيمة بديلة آمنة بدل انهيار البرنامج |
| 16 | } finally { |
| 17 | console.log("انتهت محاولة الجلب"); |
| 18 | } |
| 19 | } |