Node.js / دليل حلول تحريري
الوعود المرفوضة دون معالجة: استعادة مسؤولية العمل غير المتزامن
ابحث عن الوعود المنفصلة وضع حدا مفيدا للأخطاء وتحقق من معالجة الفشل وتنظيف الموارد.
قاعدة معرفة للقراءة فقط · بلا تعليقات عامة
تعرّف على الأعراض
تفشل عملية غير متزامنة بعد إعلان مستدعيها النجاح، أو يصل رفض إلى معالجة العملية، أو يعلق طلب بعد استثناء. في السلوك الافتراضي للإصدار الحديث المتحقق منه قد يصبح الرفض غير المعالج استثناء غير ملتقط وينهي العملية. قد تؤثر خيارات التشغيل في السياسة؛ فسجل الأمر الفعلي ولا تفترض سلوك التحذير القديم.
افهم الأسباب المحتملة
- بدأ مساعد وعدا دون إرجاعه، أو نسي المستدعي await، أو مُرر callback غير متزامن لواجهة تتجاهل وعده، أو رمى مسار الخطأ أثناء معالجة الفشل الأصلي. تلتقط try/catch المتزامنة العمل المنتظر أو المرمي ضمن تنفيذها فقط، ولا تتولى بأثر رجعي مهمة منفصلة.
تتبع سلسلة الوعد
ابدأ من المكدس وحدد منشئ الوعد الأصلي. تتبع المستدعين: هل يرجعونه أو ينتظرونه أو يتولون مهمة خلفية صراحة؟ انتبه إلى forEach مع async والمؤقتات ومستمعات EventEmitter، فقد لا ينتظر المستهلك وعدها. سجل إعادة إنتاج اصطناعية صغيرة دون بيانات حساسة. لا تضف catch فارغة في كل موضع؛ فهي تخفي الفشل وتحفظ مسار النجاح الخاطئ.
استعادة حد مفيد
يوضح المقطع غير القابل للتشغيل استدعاء منفصلا: تنتهي try المحيطة قبل ملاحظة الوعد المرفوض. ينتظر الإصلاح الكامل العملية داخل حد غير متزامن ويصنف رمزها وينفذ تنظيف finally. ضع الحد حيث يمكن تحويل الفشل إلى استجابة HTTP أو حالة أداة أو نتيجة مهمة. تضيف الدالة الدنيا سياقا وتعيد الرمي عادة ولا تدعي النجاح. احتفظ بالسبب عند التغليف كي يبقى الدليل الأصلي.
// Intentionally broken illustration; do not run.
async function save() { throw new Error('Write failed'); }
try { save(); } catch (error) { console.error(error.message); }
تعيد save وعدا مرفوضا ولا تنتظره catch المتزامنة أو تراقبه. المقطع غير قابل للتشغيل عمدا.
async function save() {
const error = new Error('Synthetic write failure'); error.code = 'WRITE_FAILED'; throw error;
}
let released = false;
try { await save(); }
catch (error) {
if (error.code !== 'WRITE_FAILED') throw error;
console.log('handled WRITE_FAILED');
} finally { released = true; }
console.log('released', released);
handled WRITE_FAILED
released true
ينتظر الحد الرفض ويعالج رمز الفشل المعروف فقط. وتعمل finally بعد المعالجة.
إدارة المجموعات والعمل الخلفي بوعي
استخدم for...of مع await للتسلسل المقصود أو تزامنا محدودا للأعمال المستقلة. تراقب Promise.all مجموعة لكنها لا تلغي الباقي عند الفشل. تحتاج المهمة الخلفية إلى مشرف يسجل النتيجة ويتولى الإلغاء والإغلاق؛ فحذف await ليس تصميما لدورة الحياة. يستخدم المثال الكامل الثاني allSettled لمجموعة صغيرة تهم نتائجها كلها. وفي الخدمة قيد المهام النشطة والانتظار بدلا من إطلاق عدد عشوائي من الوعود.
const tasks = [Promise.resolve('saved'), Promise.reject(new Error('Synthetic failure'))];
const outcomes = await Promise.allSettled(tasks);
for (const outcome of outcomes) {
console.log(outcome.status === 'fulfilled' ? `ok: ${outcome.value}` : 'failed: recorded');
}
ok: saved
failed: recorded
تُراقب النتيجتان في دورة واحدة. المهمة المرفوضة غير منفصلة ولكل نتيجة معالجة صريحة.
معاملة فشل العملية كحد حادث
تساعد خطافات الرفض والاستثناء العامة في تشخيص أخير وسياسة إغلاق منضبط، ولا تستبدل الملكية المحلية أو تبرر استئناف الخدمة بعد فشل قيد مجهول. تجنب الخروج الفوري قبل تنظيف محدود، ولا تحاول استعادة غير متزامنة معقدة من استثناء غير ملتقط دون سياسة. استخدم مديرا خارجيا لإعادة التشغيل وسو الآثار غير المؤكدة. قد تكون كتابة بعيدة قُطعت قد نجحت؛ فتحتاج المحاولة إلى منع التكرار أو فحص الحالة.
تحقق من نجاح الإصلاح
شغّل الأمثلة الكاملة وتحقق من المخرجات والخروج الطبيعي دون تحذيرات رفض. في اختبار التطبيق اجعل الاعتمادية ترفض وتحقق من النتيجة الموثقة والتنظيف مرة واحدة. اختبر النجاح والإلغاء أيضا وتأكد من عدم إعلان نجاح أو استجابة قبل اكتمال العملية المملوكة.
امنع تكرار المشكلة
- اشترط مسؤولا لكل استدعاء غير متزامن واستخدم مجموعات محدودة وعقود فشل صريحة. أضف اختبارات رجوع للعمل المنفصل وراجع الواجهات ذات callbacks قبل إعطائها async. وثّق سياسة إنهاء العملية وافصلها عن استعادة الطلب.