معالجة الأخطاء في JavaScript: try/catch وأكثر
معالجة الأخطاء هي إحدى المهام الأساسية عند التطوير باستخدام JavaScript، خاصة في بيئة Node.js، حيث يؤثر استقرار تطبيق الخادم بشكل مباشر على تجربة المستخدم. يمكن أن تؤدي الاستثناءات غير المعالجة إلى انهيار العملية بأكملها وفقدان البيانات. في هذه المقالة، سنتناول ليس فقط الكتلة التقليدية try/catch، ولكن أيضًا التقنيات المتقدمة: العمل مع finally، وإنشاء أنواع أخطاء مخصصة، ومعالجة الأخطاء غير المتزامنة، وأفضل الممارسات لـ Node.js.
1. الأساسيات: try و catch و finally
كتلة try/catch هي الآلية الأساسية لالتقاط الاستثناءات المتزامنة. إذا حدث خطأ في كتلة try، يتم نقل التنفيذ فورًا إلى catch. يتم تنفيذ كتلة finally دائمًا - بغض النظر عما إذا كان هناك خطأ أم لا. هذا مناسب لتحرير الموارد (إغلاق الملفات، الاتصالات).
const fs = require('fs');
function readConfig(path) { try { const data = fs.readFileSync(path, 'utf8'); console.log('تم تحميل التكوين'); return JSON.parse(data); } catch (err) { console.error('خطأ في قراءة التكوين:', err.message); return null; } finally { console.log('انتهت محاولة القراءة'); }}
const config = readConfig('./config.json');console.log(config); // null عند وجود خطأهام: catch يلتقط فقط الأخطاء التي حدثت في try. الأخطاء في catch أو finally تبقى غير معالجة.
2. أنواع الأخطاء في JavaScript و Node.js
في JavaScript، توجد عدة أنواع مدمجة من الأخطاء، كل منها يرث من Error:
- SyntaxError — خطأ نحوي في تحليل الكود.
- ReferenceError — الإشارة إلى متغير غير موجود.
- TypeError — نوع بيانات غير صالح (مثلاً، استدعاء شيء ليس دالة).
- RangeError — قيمة خارج النطاق المسموح به.
- URIError — خطأ عند العمل مع URI (encodeURI/decodeURI).
في Node.js، تمت إضافة AssertionError (من الوحدة assert) والأخطاء النظامية (EACCES، ENOENT وغيرها) من الوحدة fs، والتي تحتوي على رمز الخطأ في الخاصية code.
try { require('fs').readFileSync('/nonexistent');} catch (err) { if (err.code === 'ENOENT') { console.log('الملف غير موجود'); } else { console.log('خطأ آخر:', err.message); }}3. إنشاء أخطاء مخصصة
للحصول على تحكم أكثر دقة في منطق المعالجة، يُوصى بإنشاء فئات أخطاء خاصة بك، تمتد من Error. هذا يسمح بالتمييز بين أخطاء منطق الأعمال والأعطال النظامية.
class ValidationError extends Error { constructor(message, field) { super(message); this.name = 'ValidationError'; this.field = field; }}
class DatabaseError extends Error { constructor(message, query) { super(message); this.name = 'DatabaseError'; this.query = query; }}
function saveUser(user) { if (!user.email) { throw new ValidationError('البريد الإلكتروني مطلوب', 'email'); } // محاكاة خطأ في قاعدة البيانات throw new DatabaseError('انتهت مهلة الاتصال', 'INSERT INTO users');}
try { saveUser({ name: 'إيفان' });} catch (err) { if (err instanceof ValidationError) { console.log(`الحقل ${err.field}: ${err.message}`); } else if (err instanceof DatabaseError) { console.error(`خطأ في قاعدة البيانات (${err.query}): ${err.message}`); // إعادة محاولة أو تسجيل } else { console.error('خطأ غير معروف:', err); }}استخدام instanceof يسمح ببناء سلاسل من المعالجات دون اللجوء إلى التحقق من الرموز النصية.