| الحالة | الوصف |
|---|---|
| Pending | قيد الانتظار — الحالة الابتدائية |
| Fulfilled | اكتملت العملية بنجاح |
| Rejected | فشلت العملية |
الوعد (Promise) هو كائن يمثل نتيجة عملية غير متزامنة لم تكتمل بعد، لكنها ستكتمل في المستقبل — إما بنجاح (Resolved/Fulfilled) أو بفشل (Rejected). ظهرت Promises كحل أنيق لمشكلة قديمة تُسمى "جحيم الاستدعاءات المتداخلة" (Callback Hell)، حيث كانت الأكواد غير المتزامنة القديمة تتشابك في طبقات متداخلة يصعب قراءتها وصيانتها.
تمتلك كل Promise ثلاث حالات ممكنة فقط طوال دورة حياتها: Pending (قيد الانتظار، الحالة الأولية)، Fulfilled (تمت بنجاح)، أو Rejected (فشلت). بمجرد انتقال الوعد من Pending إلى أي من الحالتين الأخريين، تصبح حالته نهائية ولا يمكن أن تتغير مرة أخرى أبداً — تُسمى هذه الخاصية "الاستقرار" (Settled).
لإنشاء Promise جديد، تستخدم new Promise() وتُمرِّر لها دالة تحتوي على وسيطين: resolve وreject، وهما دالتان تستدعيهما أنت يدوياً عند اكتمال العملية بنجاح أو بفشل على التوالي. للتعامل مع نتيجة Promise موجود بالفعل (مثل تلك التي تُعيدها fetch)، تستخدم then() لتحديد ماذا يحدث عند النجاح، وcatch() لالتقاط أي خطأ يحدث في أي مرحلة من السلسلة، وfinally() لتنفيذ كود ينفَّذ دائماً بغض النظر عن النجاح أو الفشل (مثل إخفاء مؤشر تحميل).
من أقوى ميزات Promises إمكانية "التسلسل" (Chaining) — كل استدعاء لـ then() يُعيد بدوره Promise جديداً، مما يسمح بربط عدة عمليات غير متزامنة متتالية بشكل مسطح وواضح بدل التداخل العميق. كما توفر Promise.all() تنفيذ عدة Promises بالتوازي والانتظار حتى تكتمل كلها معاً (أو فشل أي منها فوراً)، وPromise.race() التي تنتظر أول Promise يكتمل فقط من بين مجموعة، بغض النظر هل نجح أم فشل.
| 1 | const وعد = new Promise((resolve, reject) => { |
| 2 | const نجح = true; |
| 3 | if (نجح) { |
| 4 | resolve("تم بنجاح!"); |
| 5 | } else { |
| 6 | reject("حدث خطأ!"); |
| 7 | } |
| 8 | }); |
| 9 | |
| 10 | وعد |
| 11 | .then(نتيجة => console.log(نتيجة)) |
| 12 | .catch(خطأ => console.error(خطأ)) |
| 13 | .finally(() => console.log("انتهت العملية")); |
مثال 1: إنشاء Promise يحاكي طلباً غير متزامن
| 1 | // محاكاة عملية غير متزامنة حقيقية (مثل طلب من خادم) |
| 2 | function جلب_بيانات_المستخدم(id) { |
| 3 | return new Promise((resolve, reject) => { |
| 4 | setTimeout(() => { |
| 5 | if (id > 0) { |
| 6 | resolve({ id, اسم: "مستخدم رقم " + id }); |
| 7 | } else { |
| 8 | reject(new Error("معرف غير صالح")); |
| 9 | } |
| 10 | }, 1000); |
| 11 | }); |
| 12 | } |
| 13 | |
| 14 | جلب_بيانات_المستخدم(5) |
| 15 | .then(مستخدم => console.log("تم الجلب:", مستخدم)) |
| 16 | .catch(خطأ => console.error("فشل:", خطأ.message)); |
مثال 2: تسلسل الوعود مقابل Promise.all المتوازي
| 1 | // تسلسل Promises وPromise.all |
| 2 | function تأخير(قيمة, مللي_ثانية) { |
| 3 | return new Promise(resolve => setTimeout(() => resolve(قيمة), مللي_ثانية)); |
| 4 | } |
| 5 | |
| 6 | // تسلسل: كل خطوة تعتمد على السابقة |
| 7 | تأخير(5, 500) |
| 8 | .then(رقم => تأخير(رقم * 2, 500)) |
| 9 | .then(نتيجة => console.log("النتيجة النهائية:", نتيجة)); // 10 |
| 10 | |
| 11 | // تنفيذ متوازٍ: كلها تعمل في نفس الوقت |
| 12 | Promise.all([تأخير("أ", 300), تأخير("ب", 500)]).then(نتائج => { |
| 13 | console.log(نتائج); // ["أ", "ب"] بعد 500ms فقط، ليس 800ms |
| 14 | }); |