مقدمة: لماذا تعتبر معالجة الأخطاء أساس الكود الموثوق
الأخطاء في الكود أمر لا مفر منه. تنشأ بسبب بيانات مستخدم غير صحيحة، أعطال الشبكة، عدم وجود ملفات على الخادم، أو أخطاء كتابية بسيطة من المطور. في بيئة Node.js، حيث يتم تنفيذ الكود بشكل غير متزامن ويتفاعل غالبًا مع موارد خارجية (قواعد البيانات، نظام الملفات، API)، يمكن أن يؤدي خطأ غير معالج إلى انهيار العملية بأكملها. المعالجة الصحيحة للأخطاء في JavaScript تحول التطبيق الهش إلى خدمة مستقرة. في هذه المقالة، سنستعرض جميع الآليات الرئيسية: من try/catch التقليدي إلى إنشاء فئات أخطاء مخصصة والعمل مع الكود غير المتزامن.
1. الأساسيات: try، catch، finally و throw
الكتلة الأساسية لمعالجة الاستثناءات في JavaScript هي بنية try...catch...finally. تسمح بـ"التقاط" الخطأ الذي يحدث في جزء "خطير" من الكود، والاستجابة له بشكل صحيح دون مقاطعة تنفيذ البرنامج بأكمله.
صيغة try/catch وكيفية عملها
يتم وضع الكود الذي قد يرمي استثناءً في كتلة try. إذا حدث خطأ، يتم نقل التحكم فورًا إلى كتلة catch، حيث يكون كائن الخطأ متاحًا. إذا لم يكن هناك خطأ، يتم تجاهل كتلة catch.
try { const data = JSON.parse('{"name": "آنا"}'); // JSON صحيح console.log(data.name); // سيُظهر: آنا} catch (error) { console.error('خطأ في التحليل:', error.message);}// يستمر التنفيذالآن لنحاكي خطأ:
try { const data = JSON.parse('json غير صالح'); // خطأ! console.log(data.name);} catch (error) { console.error('خطأ في التحليل:', error.message); // الإخراج: خطأ في التحليل: Unexpected token 'غ', ... is not valid JSON}console.log('البرنامج لا يزال يعمل!'); // هذا الكود سيتم تنفيذهكتلة finally: الكود الذي سيتم تنفيذه دائمًا
يتم تنفيذ كتلة finally بغض النظر عما إذا كان هناك خطأ أم لا. إنها مثالية لتحرير الموارد (إغلاق الملفات، اتصالات قاعدة البيانات) أو تنظيف المؤقتات.
let resource = null;try { resource = acquireResource(); // الحصول على المورد resource.doSomething(); // عملية خطيرة} catch (error) { console.error('خطأ:', error);} finally { if (resource) { resource.close(); // دائمًا نغلق المورد console.log('تم تحرير المورد.'); }}عامل throw: إنشاء أخطائك الخاصة
يمكنك أنت بنفسك "رمي" خطأ باستخدام throw. هذا مفيد للتحقق من صحة البيانات المدخلة أو مقاطعة التنفيذ عند انتهاك منطق الأعمال. يمكن تمرير أي شيء كوسيطة، لكن أفضل ممارسة هي استخدام كائن Error أو ورثته.
function divide(a, b) { if (typeof a !== 'number' || typeof b !== 'number') { throw new Error('يجب أن تكون الوسيطات أرقامًا'); } if (b === 0) { throw new Error('القسمة على صفر ممنوعة'); } return a / b;}
try { console.log(divide(10, 0));} catch (error) { console.error('خطأ:', error.message); // خطأ: القسمة على صفر ممنوعة}2. تقنيات متقدمة: فئات الأخطاء المخصصة والتصنيف
في المشاريع الكبيرة، لا يكفي استخدام Error القياسي فقط. يجب التمييز بين أخطاء الشبكة، التحقق من الصحة، التفويض وغيرها. لهذا، يتم إنشاء فئات مخصصة توسع Error المدمج.
إنشاء أخطاء مخصصة
class ValidationError extends Error { constructor(message) { super(message); this.name = 'ValidationError'; // اسم فريد للتحقق }}
class NetworkError extends Error { constructor(message, statusCode) { super(message); this.name = 'NetworkError'; this.statusCode = statusCode; }}
function fetchUser(id) { if (!id) { throw new Valida