البرمجة

فهم الوعود في جافاسكربت

الوعود في جافاسكربت (Promises)

الوعود (Promises) في جافاسكربت هي آلية تُستخدم لإدارة العمليات غير المتزامنة بطريقة فعّالة، بحيث تُعتبر بديلاً للأطر التقليدية مثل الـcallbacks (الدوال الاستدعائية). مع تطور جافاسكربت وتزايد استخدام التطبيقات المعتمدة على الإنترنت، أصبحت الحاجة إلى التعامل مع العمليات غير المتزامنة أكثر أهمية. يعالج الـ Promise مشكلة “الجحيم العكسي” (Callback Hell)، حيث يؤدي استخدام الـcallbacks المتداخل إلى تعقيد الشيفرة البرمجية وصعوبة صيانتها.

تعد الوعود أحد الأدوات الحديثة التي تمنح المطورين طريقة أكثر تنظيمًا لكتابة الأكواد المرتبطة بالعمليات غير المتزامنة، مثل جلب البيانات من الخوادم أو قراءة ملفات.

مفهوم الوعود (Promise)

عند الحديث عن الوعود في جافاسكربت، يمكن تمثيل الـPromise ككائن يحتوي على نتيجة محتملة للعملية غير المتزامنة في المستقبل، والتي قد تكون إما ناجحة أو فاشلة. الوعود تساعد في كتابة أكواد نظيفة وسهلة القراءة عبر تقديم حل للمشاكل المرتبطة بـcallbacks المتداخلة.

الـPromise يُمكنه أن يكون في أحد ثلاث حالات:

  1. معلق (Pending): هو الحالة الابتدائية التي يبدأ بها الـPromise. في هذه المرحلة، لم يتم الانتهاء من العملية بعد.

  2. مُنجز (Fulfilled): عندما تكتمل العملية بنجاح ويتم تحديد القيمة.

  3. مرفوض (Rejected): عندما تواجه العملية خطأً، ويتم تحديد سبب الفشل.

كيفية إنشاء واستخدام Promise

يمكن إنشاء الـPromise باستخدام المُنشئ (Constructor) new Promise(). يأخذ هذا المُنشئ دالة كوسيط تُسمى “دالة التنفيذ” (Executor)، وتحتوي على وسيطين رئيسيين هما resolve و reject.

  • resolve: يستخدم عند إتمام العملية بنجاح، ويحدد القيمة النهائية للـPromise.

  • reject: يستخدم عندما تفشل العملية، ويحدد السبب الذي أدى إلى الفشل.

إليك مثالًا بسيطًا على كيفية استخدام Promise:

javascript
let promise = new Promise(function(resolve, reject) { let success = true; if (success) { resolve("تمت العملية بنجاح!"); } else { reject("حدث خطأ أثناء العملية."); } }); promise.then(function(result) { console.log(result); // "تمت العملية بنجاح!" }).catch(function(error) { console.log(error); // "حدث خطأ أثناء العملية." });

استخدامات الـ Promise

الوعود غالبًا ما تستخدم في التعامل مع العمليات غير المتزامنة مثل:

  1. الاتصال بالخوادم (API Calls): عند إجراء استعلامات AJAX أو استخدام مكتبات مثل Fetch للحصول على بيانات من الخوادم.

  2. العمليات التي تتطلب وقتًا طويلًا: مثل تحميل الملفات أو التعامل مع قواعد البيانات.

  3. الأنظمة التي تعتمد على الاستجابة المتأخرة: مثل الرسائل الفورية أو المعاملات المالية.

التعامل مع Promise باستخدام .then() و .catch()

للتعامل مع النتائج الناتجة عن الـPromise، يُستخدم الميثود .then(). يمكن استخدام .then() لسلسلة العمليات التي يجب تنفيذها عند إتمام الـPromise بنجاح.

مثال على استخدام .then():

javascript
let promise = new Promise(function(resolve, reject) { resolve("تمت العملية بنجاح!"); }); promise.then(function(result) { console.log(result); // سيتم طباعة "تمت العملية بنجاح!" });

إذا كانت العملية فاشلة، يُمكنك استخدام .catch() للتعامل مع الأخطاء:

مثال على استخدام .catch():

javascript
let promise = new Promise(function(resolve, reject) { reject("حدث خطأ."); }); promise.catch(function(error) { console.log(error); // سيتم طباعة "حدث خطأ." });

سلسلة الوعود (Chaining Promises)

من الميزات القوية للـPromises هي القدرة على ربط العديد من العمليات غير المتزامنة معًا باستخدام سلسلة من الـthen()، مما يعزز من تنظيم الكود ويسهل القراءة والصيانة.

مثال على سلسلة Promises:

javascript
let promise = new Promise(function(resolve, reject) { resolve("العملية الأولى تمت بنجاح!"); }); promise.then(function(result) { console.log(result); // "العملية الأولى تمت بنجاح!" return "العملية الثانية تمت بنجاح!"; }).then(function(result) { console.log(result); // "العملية الثانية تمت بنجاح!" }).catch(function(error) { console.log(error); // في حالة وجود خطأ في أي خطوة });

استخدام Promise.all() و Promise.race()

1. Promise.all()

يُستخدم Promise.all() لانتظار مجموعة من الـPromises لتنتهي جميعًا بنجاح. إذا فشل أي منها، سيتم رفض الـPromise المجمعة.

مثال:

javascript
let promise1 = Promise.resolve(3); let promise2 = Promise.resolve(4); let promise3 = Promise.resolve(5); Promise.all([promise1, promise2, promise3]).then(function(values) { console.log(values); // [3, 4, 5] });

2. Promise.race()

يُستخدم Promise.race() عندما نحتاج إلى معرفة أي Promise ينتهي أولًا. يمكن أن يكون مفيدًا في الحالات التي لا نهتم فيها بانتظار جميع الـPromises، بل فقط أول واحدة تنتهي.

مثال:

javascript
let promise1 = new Promise((resolve, reject) => setTimeout(resolve, 100, 'تمت العملية 1')); let promise2 = new Promise((resolve, reject) => setTimeout(resolve, 200, 'تمت العملية 2')); Promise.race([promise1, promise2]).then((result) => { console.log(result); // سيتم طباعة "تمت العملية 1" لأن promise1 انتهت أولاً. });

التعامل مع الأخطاء في Promises

أحد أهم مزايا استخدام الـPromise هو طريقة التعامل مع الأخطاء بطريقة مهنية مقارنة بـcallbacks. يمكن استخدام .catch() أو .then() مع دالة الخطأ الخاصة به (المعروفة بـonRejected).

مثال على التعامل مع الأخطاء:

javascript
let promise = new Promise(function(resolve, reject) { reject("حدث خطأ."); }); promise.catch(function(error) { console.log("تم القبض على الخطأ: " + error); // "تم القبض على الخطأ: حدث خطأ." });

الفرق بين الـCallbacks والـPromises

في جافاسكربت، كانت الـcallbacks هي الطريقة الرئيسية لمعالجة العمليات غير المتزامنة. ولكن مع تزايد تعقيد التطبيقات، أصبح التعامل مع الـcallbacks أكثر صعوبة. يتسبب استخدام العديد من الـcallbacks في فوضى تسمى “جحيم الـcallbacks” (Callback Hell)، حيث يصعب متابعة التسلسل المنطقي للعمليات.

لكن مع الـPromises، يمكن كتابة أكواد غير متزامنة بطريقة سلسة ونظيفة باستخدام .then() و .catch()، مما يسهل قراءة الكود وصيانته. بالإضافة إلى أن Promises تدير الأخطاء بشكل أفضل، حيث يمكن التعامل مع الخطأ في مكان واحد، بدلاً من إضافة معالجات للأخطاء في كل callback على حدة.

الوعود المتسلسلة مع async/await

مع تطور جافاسكربت، ظهرت تقنية async/await التي توفر طريقة أكثر سهولة في التعامل مع الـPromises، حيث تتيح كتابة الكود غير المتزامن بنفس طريقة كتابة الكود المتزامن. يُعدّ async/await تطورًا للـPromises، حيث يمكن للمطورين كتابة الأكواد بشكل أكثر وضوحًا وسهولة.

مثال على استخدام async/await:

javascript
async function fetchData() { try { let response = await fetch('https://jsonplaceholder.typicode.com/posts'); let data = await response.json(); console.log(data); } catch (error) { console.log('حدث خطأ:', error); } } fetchData();

في هذا المثال، نلاحظ أن الكود يكتب بطريقة متسلسلة وسهلة الفهم، حيث يبدو أن العمليات المتزامنة تتم واحدة تلو الأخرى، لكن في الواقع هي غير متزامنة وتنتظر انتهاء كل عملية.

الخاتمة

الوعود (Promises) في جافاسكربت تعد من الأدوات الأساسية لإدارة العمليات غير المتزامنة بطريقة أكثر كفاءة وتنظيمًا. من خلال الـPromises، يمكن تجنب المشاكل الناتجة عن استخدام الـcallbacks المتداخلة، مما يجعل الكود أكثر قابلية للصيانة والقراءة. مع تزايد الاستخدامات في التطبيقات الحديثة، أصبح الـPromise أداة لا غنى عنها لأي مطور جافاسكربت.