مقدمة: لماذا معالجة الأخطاء هي أساس الكود الموثوق
أي تطبيق يعمل على Node.js، عاجلاً أم آجلاً، سيواجه حالات استثنائية: إدخال مستخدم غير صحيح، تعطل الشبكة، عدم وجود ملف على القرص، أو بيانات غير صحيحة من قاعدة البيانات. بدون معالجة صحيحة للأخطاء، تؤدي هذه الحالات إلى تعطل الخادم، تسرب الذاكرة، أو سلوك غير محدد. في JavaScript، خاصة في بيئة Node.js، تعتبر آلية try/catch أداة أساسية لبناء أنظمة متسامحة مع الأخطاء. في هذه المقالة، سنشرح ليس فقط try/catch التقليدي، ولكن أيضاً التقنيات المتقدمة: فئات الأخطاء المخصصة، العمل مع الـ Promises والدوال غير المتزامنة، وأفضل ممارسات التسجيل.
الأساسيات: كتل try و catch و finally
الصيغة الأساسية لمعالجة الأخطاء في JavaScript تبدو كالتالي:
try { // كود قد يرمي استثناءً const data = JSON.parse('{"name": "Node.js"}'); console.log(data.name);} catch (error) { // معالجة الخطأ console.error('حدث خطأ:', error.message);} finally { // كود سيتم تنفيذه في كل الأحوال console.log('تم تنفيذ كتلة finally');}كيف يعمل هذا:
try— يحتوي على كود يحتمل أن يكون خطيراً.catch— يلتقط الاستثناء إذا حدث. في المعاملerrorيتم وضع كائن الخطأ.finally— يتم تنفيذه دائماً، بغض النظر عما إذا كان هناك خطأ أم لا. مثالي لإغلاق الملفات، تحرير الموارد، أو إنهاء المعاملات.
من المهم أن تتذكر: finally سيعمل حتى بعد return في كتلة try أو catch.
أنواع الأخطاء في JavaScript
منشئات الأخطاء المضمنة تساعد في تصنيف الاستثناءات بشكل أكثر دقة:
Error— الفئة الأساسية لجميع الأخطاء.SyntaxError— خطأ نحوي (مثل JSON غير صحيح).TypeError— نوع بيانات غير صحيح (مثل استدعاء شيء ليس دالة).ReferenceError— الإشارة إلى متغير غير موجود.RangeError— تجاوز الحدود المسموح بها (مثل العودية).
مثال على معالجة أنواع مختلفة:
try { const obj = null; console.log(obj.property); // TypeError} catch (err) { if (err instanceof TypeError) { console.log('خطأ في النوع:', err.message); } else if (err instanceof ReferenceError) { console.log('المتغير غير موجود'); } else { console.log('خطأ غير معروف:', err); }}توليد الأخطاء الخاصة بك: عامل throw
أحياناً يكون من الضروري الإشارة صراحةً إلى وجود حالة استثنائية في البرنامج. لهذا يستخدم عامل throw. يمكنه رمي أي شيء: سلسلة نصية، رقم، كائن، لكن الأفضل هو استخدام نسخة من فئة Error أو وريثها.
function divide(a, b) { if (b === 0) { throw new Error('القسمة على صفر ممنوعة'); } if (typeof a !== 'number' || typeof b !== 'number') { throw new TypeError('الوسائط يجب أن تكون أرقاماً'); } return a / b;}
try { const result = divide(10, 0); console.log(result);} catch (err) { console.error(err.name + ': ' + err.message);}لماذا من الأفضل رمي Error بدلاً من سلسلة نصية؟ كائن Error يحتوي على مكدس الاستدعاءات (stack)، وهو أمر بالغ الأهمية لتصحيح الأخطاء في Node.js. السلسلة النصية لن توفر هذه المعلومات.
إنشاء فئات أخطاء مخصصة
في المشاريع الحقيقية (مثل خادم ويب على Express)، من المناسب أن يكون لديك أنواع أخطاء خاصة بك لمواقف مختلفة: ValidationError، NotFoundError، DatabaseError. هذا يبسط المعالجة المركزية.
class ValidationError extends Error {