معالجة الأخطاء في JavaScript: try/catch وما بعدها: المستوى المتقدم
معالجة الأخطاء ليست مجرد حماية من تعطل التطبيق، بل هي جانب أساسي لبناء أنظمة موثوقة ومتسامحة مع الأخطاء في JavaScript و Node.js. الاستخدام الأساسي لـ try/catch مألوف لكل مطور، ولكن في المشاريع الحقيقية، خاصة على جانب الخادم، تظهر العديد من التفاصيل الدقيقة: التدفقات غير المتزامنة، سلاسل البروميس، الأعطال العالمية، والحاجة إلى أخطاء مخصصة منظمة. في هذه المقالة، سنستعرض التقنيات المتقدمة لمعالجة الأخطاء التي ستساعدك على كتابة كود أنظف وأكثر قابلية للتنبؤ في Node.js.
1. الغوص العميق في try/catch وعدم التزامن
إن try/catch التقليدي متزامن. إنه يلتقط الأخطاء داخل الدوال العادية بشكل ممتاز، لكنه يفشل أمام الاستدعاءات غير المتزامنة أو البروميس بدون await. في Node.js، حيث 90% من الكود غير متزامن، غالبًا ما يصبح هذا سببًا لـ "ابتلاع" الاستثناءات.
خطأ: try/catch لا يلتقط الخطأ في setTimeout
function dangerousAsync() { setTimeout(() => { throw new Error('خطأ في المؤقت!'); }, 1000);}
try { dangerousAsync();} catch (e) { console.log('تم الالتقاط:', e.message); // لن يتم تنفيذ هذا الكود}في هذا المثال، يتم طرح الخطأ خارج سياق try، وسيتم إنهاء عملية Node.js بشكل غير طبيعي. النهج الصحيح هو استخدام البروميس أو async/await.
الحل: تغليف الكود غير المتزامن في Promise
function safeAsync() { return new Promise((resolve, reject) => { setTimeout(() => { reject(new Error('خطأ في المؤقت!')); }, 1000); });}
(async () => { try { await safeAsync(); } catch (e) { console.log('تم الالتقاط:', e.message); // سيتم تنفيذه بعد ثانية واحدة }})();القاعدة الأساسية: قم بتغليف أي عملية غير متزامنة في بروميس، واستخدم await داخل try/catch. هذا يضمن التقاط الخطأ في السياق الصحيح.
2. فئات الأخطاء المخصصة: الدلالات والسياق
بدلاً من طرح new Error('حدث خطأ ما')، قم بإنشاء تسلسل هرمي لفئات الأخطاء الخاصة بك. هذا يسمح لمستوى المعالج بفهم نوع العطل والاستجابة وفقًا لذلك (على سبيل المثال، التسجيل، الإرسال للمراقبة، أو إعادة حالة HTTP محددة للمستخدم).
class AppError extends Error { constructor(message, statusCode = 500) { super(message); this.name = this.constructor.name; this.statusCode = statusCode; Error.captureStackTrace(this, this.constructor); }}
class ValidationError extends AppError { constructor(message) { super(message, 400); }}
class DatabaseError extends AppError { constructor(message, originalError) { super(message, 503); this.originalError = originalError; // حفظ الخطأ الأصلي لتصحيح الأخطاء }}
// الاستخدامfunction findUser(id) { if (!id) throw new ValidationError('معرف المستخدم مطلوب'); // ... منطق قد يطرح DatabaseError}مزايا هذا النهج:
- قابلية القراءة: اسم الفئة يخبرنا فورًا بطبيعة المشكلة.
- التوجيه: في المعالج العالمي، يمكنك التحقق من
instanceofواختيار استراتيجية الاستجابة. - حقول إضافية: يمكنك إضافة أي بيانات (
statusCode،originalError،userId، إلخ).
3. معالجات الأخطاء العالمية في Node.js
حتى مع أعلى درجات التحكم، يمكن أن تتسرب الأخطاء غير المعالجة إلى الخارج. في Node.js، هناك حدثان رئيسيان لالتقاطها: uncaughtException و unhandledRejection
المدونات
توصيات الكتب
اقرأ أيضًا