كيفية التعامل مع reject بشكل صحيح في async/await؟ تحليل باستخدام TypeScript

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

Async/Await و reject: لماذا هي مشكلة للمبتدئين؟

\n

هل سبق لك أن كتبت دالة async، وانتظرت ردًا من الخادم، ثم واجهت خطأ UnhandledPromiseRejection؟ مألوف؟ هذه واحدة من أكثر المشكلات شيوعًا التي يواجهها المطورون عند الانتقال إلى async/await في TypeScript.

\n\n

على Stack Overflow، حصد سؤال "How to reject in async/await syntax?" آلاف المشاهدات. وليس من دون سبب. للوهلة الأولى، يبدو أن مجرد إرجاع Promise.reject() كافٍ لحل المشكلة. لكن في المشاريع الحقيقية، الأمور أكثر تعقيدًا: الأنواع، سلاسل الاستدعاءات، فقدان سياق الأخطاء.

\n\n

في هذا الدليل، سنحلل كيفية رفض البروميسات بشكل صحيح في دوال async في TypeScript، ونتعلم التمييز بين throw و return Promise.reject()، ونلقي نظرة على أمثلة عملية من مشاريع حقيقية.

\n\n

الأساسيات: كيف يعمل reject في async/await

\n

قبل الغوص في التفاصيل، دعنا نسترجع الأساسيات. أي دالة async في TypeScript تُرجع Promise. إذا حدث throw داخلها أو تم إرجاع Promise.reject()، ينتقل البروميس إلى حالة rejected.

\n\n
// مثال بسيط على reject في async/await\nasync function fetchUser(id: number): Promise<User> {\n    if (id <= 0) {\n        throw new Error('معرف المستخدم غير صالح');\n    }\n    // ...منطق جلب المستخدم\n}
\n\n

هنا، throw يتحول تلقائيًا إلى reject. لكن ماذا لو أردنا إرجاع بروميس مرفوض جاهز؟

\n\n

throw مقابل return Promise.reject()

\n

في TypeScript، كلا الأسلوبين يعملان، لكن هناك فروق دقيقة:

\n\n
    \n
  • throw — أسلوب أكثر اصطلاحية. يوقف تنفيذ الدالة وينقل التحكم إلى أقرب catch.
  • \n
  • return Promise.reject() — يُرجع صراحة بروميسًا مرفوضًا. مفيد عندما تحتاج إلى تمرير خطأ من دالة أخرى.
  • \n
\n\n
// الفرق بين throw و return Promise.reject()\nasync function processOrder(orderId: string): Promise<OrderStatus> {\n    // الخيار 1: throw\n    if (!orderId) {\n        throw new Error('معرف الطلب مطلوب');\n    }\n    \n    // الخيار 2: return Promise.reject\n    if (orderId.startsWith('test_')) {\n        return Promise.reject(new Error('الطلبات التجريبية غير مسموح بها'));\n    }\n    \n    return { status: 'processing' };\n}
\n\n

هام: لا يستنتج TypeScript الأنواع بشكل صحيح دائمًا عند استخدام return Promise.reject(). إذا كانت الدالة تتوقع Promise<T> وقمت بإرجاع Promise.reject()، فقد يحدث خطأ في كتابة الأنواع. في هذه الحالات، من الأفضل استخدام throw.

\n\n

الأخطاء الشائعة عند استخدام reject في دوال async

\n

حتى المطورون ذوو الخبرة في TypeScript يرتكبون أحيانًا أخطاء كلاسيكية. إليك أهم 3 أخطاء:

\n\n

1. نسيان معالجة reject في الكود المستدعي

\n
async function main() {\n    // سيء: لا يوجد try/catch\n    const user = await fetchUser(-1);\n    console.log(user); // UnhandledPromiseRejection!\n}\n\n// جيد:\nasync function mainSafe() {\n    try {\n        const user = await fetchUser(-1);\n        console.log(user);\n    } catch (error) {\n        console.error('فشل في جلب المستخدم:', error);\n    }\n}
\n\n

2. كتابة أنواع الأخطاء بشكل غير صحيح

\n

في TypeScript، كتلة catch تلتقط unknown افتراضيًا. هذا صحيح، لكن أحيانًا يحاول المطورون الوصول إلى error.message دون التحقق.

\n\n
try {\n    await riskyOperation();\n} catch (error) {\n    // خطأ: error من نوع unknown\n    console.log(error.message); // ❌ الخاصية 'message' غير موجودة في النوع 'unknown'\n    \n    // صحيح:\n    if (error instanceof Error) {\n        console.log(error.message);\n    } else {\n        console.log                

المدونات

توصيات الكتب