Menu
flag Ar iconالعربيةdown icon

Promise في TypeScript: Promise<T> وPromise.all وallSettled

كيف تُحدد أنواع الـ promises في TypeScript: النوع Promise<T>، وتحديد نوع new Promise وresolve، وكيف تغيّر then النوع، ولماذا تعطيك catch النوع any، وPromise.all مع نتائج على شكل tuple، وأنواع نتائج Promise.allSettled، وتغليف واجهات callback بـ promise محدد النوع.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

في TypeScript يكون نوع الـ promise هو Promise<T>، حيث T نوع القيمة التي يُحل بها. الدالة التي تعيد promise تصرّح بذلك في نوع القيمة المُعادة، والقيمة التي تحصل عليها من then أو await نوعها T.

آليات الـ promise (الحالات، وطابور المهام الدقيقة، والتسلسل) هي JavaScript عادية، مشروحة في الـ promises في JavaScript. هذه الصفحة عن الأنواع.

تحديد نوع new Promise وresolve

عندما يكون new Promise القيمة المُعادة من دالة لها نوع إعادة مصرّح به، يأتي T من ذلك النوع. أما وحده فلا يستنتج TypeScript النوع T من استدعاءات resolve، فتحصل على Promise<unknown>. مرّر وسيط النوع صراحة:

تفصيلان. نوع resolve مأخوذ من T، لذلك يكون resolve("42") في Promise<number> خطأ ترجمة. واستدعاء resolve() دون وسيط مسموح فقط عندما يتضمن T النوع void: في Promise<number> يكون الخطأ TS2794 (Expected 1 arguments, but got 0. Did you forget to include 'void' in your type argument to 'Promise'?).

جانب reject ليس محدد النوع. لا يوجد Promise<T, E>: يمكن أن يُرفض الـ promise بأي قيمة.

then وcatch وfinally

كل then تعيد promise جديدًا نوعه مأخوذ مما تعيده دالتها. إذا أعادت الدالة promise يفكّه TypeScript، فلا تحصل أبدًا على Promise<Promise<T>>.

معامل دالة catch نوعه any في المكتبة القياسية، لا unknown. لا شيء يمنعك من كتابة e.message على قيمة قد تكون نصًا. حدد نوعه كـ unknown، كما في المثال، وضيّقه قبل الاستخدام. أما try/catch حول await فأفضل: مع strict يكون متغير catch فيها unknown أصلًا (راجع معالجة الأخطاء).

Promise.all يعيد Tuple

عند تمرير مصفوفة حرفية من promises مختلفة الأنواع، يعيد Promise.all promise لـ tuple، ويحتفظ كل موضع فيه بنوعه الخاص:

يمكن خلط قيم ليست promises، وتمر كما هي دون تغيير. يُرفض Promise.all بمجرد رفض أحد المُدخلات، وتضيع النتائج الأخرى. يُحل Promise.race أو يُرفض بأول مُدخل يستقر، ونوعه union المُدخلات؛ ويُحل Promise.any بأول نجاح، ولا يُرفض بـ AggregateError إلا إذا فشلت كل المُدخلات.

Promise.allSettled ونوع نتيجته

ينتظر Promise.allSettled كل المُدخلات ولا يُرفض أبدًا. كل نتيجة من النوع PromiseSettledResult<T>، وهو union تضيّقه بالحقل status:

لا يوجد r.value إلا بعد فحص status، لأن الحالة المرفوضة ليس فيها value. ونوع reason هو any، للسبب نفسه كما في catch. تستخدم filter دالة type predicate حتى تُحدد المصفوفة الناتجة كنتائج ناجحة.

تغليف واجهة Callback في Promise

الواجهات الأقدم تُبلغ عن النتائج عبر دالة callback، غالبًا بأسلوب Node (err, result) => void. غلّفها مرة واحدة في دالة تعيد promise محدد النوع، ويستطيع بقية الكود استخدام await:

في Node يقوم util.promisify بذلك للدوال التي تتبع اصطلاح (err, result)، وكثير من الوحدات المدمجة لها أصلًا نسخ تعمل بالـ promises (node:fs/promises، node:timers/promises).

أخطاء شائعة

  • إعادة T حيث يقول التوقيع Promise<T>، أو العكس. الدالة غير المتزامنة المصرّح بأنها تعيد Promise<User> يجب أن تعيد promise؛ أما دالة async فتعيد واحدًا تلقائيًا.
  • نسيان التعامل مع promise. استدعاء مثل save(user); دون await أو then أو catch يُترجم دون مشكلة، ويصبح الرفض رفضًا غير معالج (ينهي عملية Node افتراضيًا). قاعدة typescript-eslint no-floating-promises تكتشف هذه الحالات.
  • الثقة بنوع .catch((e) => ...). المتغير e فيها نوعه any. حدد نوعه كـ unknown.
  • استخدام new Promise حول شيء يعيد promise أصلًا. أعد الـ promise الموجود أو استخدم await معه مباشرة.

الأسئلة الشائعة

ما هو Promise<T> في TypeScript؟

Promise<T> هو نوع promise يُحل بقيمة من النوع T. الدالة التي تعيد Promise<string> تعطيك promise تحصل منه دالة then أو await على string. والـ promise الذي يُحل دون قيمة نوعه Promise<void>.

كيف أحدد نوع new Promise في TypeScript؟

مرّر وسيط النوع: new Promise<number>((resolve, reject) => ...). دونه لا يستطيع TypeScript استنتاج القيمة من استدعاءات resolve وتكون النتيجة Promise<unknown>. للـ promise الذي يُحل دون شيء استخدم new Promise<void>(resolve => ...) حتى يُسمح باستدعاء resolve() دون وسيط.

ما نوع الخطأ في catch الخاصة بالـ promise؟

any. المعامل reason في دالة catch نوعه any في المكتبة القياسية، لأن أي شيء يمكن أن يُرمى أو يُرفض به. حدد نوعه بنفسك كـ unknown (.catch((e: unknown) => ...)) وضيّقه بـ instanceof Error قبل استخدامه.

كيف يعمل Promise.all مع الأنواع في TypeScript؟

مع مصفوفة حرفية يعيد Promise.all نوع tuple فيه عنصر لكل مُدخل، بالترتيب: Promise.all([getUser(), getCount()]) نوعه Promise<[User, number]>، فيعطي التفكيك كل قيمة نوعها الخاص. ومع مصفوفة من نوع واحد، T[]، يعيد Promise<T[]>.

ما الفرق بين Promise.all وPromise.allSettled؟

يُرفض Promise.all بمجرد رفض أي مُدخل. أما Promise.allSettled فيُحل دائمًا، بمصفوفة من كائنات PromiseSettledResult<T>: { status: "fulfilled", value } أو { status: "rejected", reason }. افحص status لتضييق كل واحد منها.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن