سلسلة الوعود (Promises Chaining) في جافا سكربت: شرح مفصل
في عالم البرمجة بلغة جافا سكربت، تُعتبر الوعود (Promises) من أبرز الأدوات المستخدمة في التعامل مع العمليات غير المتزامنة (Asynchronous). مع تطور الجافا سكربت، ظهرت الحاجة إلى تحسين الطريقة التي يتعامل بها المطورون مع العمليات غير المتزامنة، مثل تحميل البيانات من الخوادم، مما أدى إلى ظهور الوعود. أحد المفاهيم المتقدمة التي ظهرت في التعامل مع الوعود هو سلسلة الوعود أو Promises Chaining. هذا المقال يهدف إلى تقديم شرح شامل لهذا الموضوع، بحيث يتناول المبدأ الأساسي، كيفية العمل، الأمثلة العملية، وكيفية الاستفادة من سلسلة الوعود في المشاريع الحديثة.
ما هي الوعود (Promises) في جافا سكربت؟
قبل أن نتناول كيفية عمل سلسلة الوعود، من المهم أولاً فهم أساسيات الوعود في جافا سكربت. الوعود هي كائنات (Objects) تمثل القيمة التي قد تكون موجودة الآن أو في المستقبل، وهي تعبر عن عملية غير متزامنة سيتم حلها لاحقًا. يمكن للوعود أن تكون في واحدة من ثلاث حالات:
-
معلقة (Pending): حيث لم تكتمل العملية بعد.
-
مكتملة (Resolved/Fulfilled): حيث تم تنفيذ العملية بنجاح.
-
مرفوضة (Rejected): حيث فشلت العملية في التنفيذ.
لإنشاء وعد في جافا سكربت، يتم استخدام الكلمة المفتاحية Promise، ويتم تمرير دالة تحتوي على معاملين: resolve و reject، حيث resolve يتم استدعاؤه عند نجاح العملية، وreject يتم استدعاؤه عند فشلها.
مثال بسيط على إنشاء وعد:
javascriptlet promise = new Promise((resolve, reject) => {
let success = true;
if (success) {
resolve("تم تنفيذ العملية بنجاح!");
} else {
reject("فشلت العملية.");
}
});
ما هو مفهوم سلسلة الوعود (Promises Chaining)؟
تتعدد العمليات غير المتزامنة في البرمجة، وقد تكون متداخلة أو تتطلب تنفيذ عمليات واحدة تلو الأخرى. لذلك، ظهرت الحاجة إلى “سلسلة الوعود” التي تسمح بترتيب العمليات غير المتزامنة بحيث يتم تنفيذها واحدة تلو الأخرى دون الحاجة إلى استخدام “التداخل” (callback hell)، مما يؤدي إلى جعل الكود أكثر نظافة ووضوحًا.
سلسلة الوعود هي ببساطة سلسلة من الوعود التي يتم تنفيذها في تسلسل، حيث يعتمد كل وعد لاحق على نتيجة الوعد السابق. يتم تنفيذ الوعود باستخدام دالة then، التي يمكن أن تُستخدم لتحديد ما يجب فعله عندما يُحل الوعد بنجاح.
كيفية عمل سلسلة الوعود؟
بشكل عام، يُمكنك بناء سلسلة من الوعود باستخدام الدالة then. يتم استدعاء then على الوعد عندما يتم حل الوعد بنجاح. يمكن استخدام then أيضًا على الوعود المتتالية، حيث تُنفذ العمليات واحدة تلو الأخرى بناءً على نتيجة الوعد السابق.
إليك مثالًا عمليًا عن كيفية استخدام سلسلة الوعود في جافا سكربت:
javascriptlet promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("العملية 1 نجحت"), 1000);
});
let promise2 = new Promise((resolve, reject) => {
setTimeout(() => resolve("العملية 2 نجحت"), 1500);
});
promise1
.then(result => {
console.log(result); // سيعرض: العملية 1 نجحت
return promise2; // سيعيد وعد جديد
})
.then(result => {
console.log(result); // سيعرض: العملية 2 نجحت
})
.catch(error => {
console.error(error); // في حال حدوث أي خطأ في السلسلة
});
في هذا المثال، يتم أولاً تنفيذ promise1 وبعد أن يتم حلها (أي بعد مرور ثانية واحدة)، يتم تنفيذ promise2. عند اكتمال كلا الوعدين، يتم طباعة النتيجة. إذا فشل أي وعد في السلسلة، فإننا نلتقط الخطأ باستخدام catch.
كيف تساعد سلسلة الوعود في التعامل مع العمليات غير المتزامنة؟
في السابق، كانت معالجة العمليات غير المتزامنة تتم عن طريق الدوال العكسية (Callbacks)، ولكن مع تطور جافا سكربت، أصبح من الأفضل استخدام الوعود (Promises) لأنها توفر طريقة أكثر نظافة ووضوحًا لتنظيم الكود. لكن الأهم من ذلك هو أن سلسلة الوعود (Promises Chaining) تقدم مزايا إضافية مثل:
-
إزالة التداخل (Callback Hell): مع سلسلة الوعود، لا يتطلب الأمر وضع دوال داخل دوال أخرى كما يحدث في التداخل. وبالتالي، يكون الكود أكثر تنظيمًا وسهولة في القراءة.
-
تسلسل العمليات: يمكن تنفيذ العديد من العمليات غير المتزامنة بترتيب معين، حيث يعتمد كل وعد على الوعد الذي قبله.
-
التعامل مع الأخطاء بشكل موحد: عند حدوث خطأ في أي وعد في السلسلة، يمكن استخدام
catchفي النهاية لالتقاط الخطأ بدلاً من وضعه في كل وعد على حدة. -
إدارة العمليات المتعددة: يمكن تنفيذ عدة عمليات غير متزامنة في وقت واحد باستخدام
Promise.all()أوPromise.race().
أمثلة عملية على سلسلة الوعود
-
مثال بسيط مع دالة
then:لنفترض أننا نقوم بتحميل بيانات من خادم ويب باستخدام وعد، ثم نقوم بمعالجة هذه البيانات في سلسلة من الوعود:
javascriptfunction fetchData() { return new Promise((resolve, reject) => { setTimeout(() => { let data = { name: "مستخدم", age: 30 }; resolve(data); }, 1000); }); } fetchData() .then(data => { console.log("تم الحصول على البيانات:", data); data.age += 1; return data; }) .then(updatedData => { console.log("تم تحديث البيانات:", updatedData); }) .catch(error => { console.error("حدث خطأ:", error); });في هذا المثال، نقوم أولاً بجلب البيانات باستخدام
fetchData، ثم نقوم بتحديث البيانات داخل سلسلة من الوعود. -
مثال مع
Promise.all():في بعض الحالات، قد تحتاج إلى تنفيذ عدة عمليات غير متزامنة في وقت واحد، ومن ثم التعامل مع النتيجة بمجرد أن تنتهي جميع العمليات. هنا يأتي دور
Promise.all():javascriptlet promise1 = new Promise((resolve, reject) => { setTimeout(() => resolve("العملية 1 انتهت"), 1000); }); let promise2 = new Promise((resolve, reject) => { setTimeout(() => resolve("العملية 2 انتهت"), 2000); }); Promise.all([promise1, promise2]) .then(results => { console.log(results); // سيعرض: ['العملية 1 انتهت', 'العملية 2 انتهت'] }) .catch(error => { console.error("حدث خطأ:", error); });في هذا المثال، نبدأ تنفيذ العمليات في نفس الوقت، وعندما تنتهي جميع العمليات، يتم التعامل مع النتيجة في
then. -
مثال مع
Promise.race():في بعض الحالات، قد ترغب في التعامل مع أول وعد يتم حلّه أولاً، بغض النظر عن الوعود الأخرى. هذا يتم باستخدام
Promise.race():javascriptlet promise1 = new Promise((resolve, reject) => { setTimeout(() => resolve("العملية 1 انتهت"), 1000); }); let promise2 = new Promise((resolve, reject) => { setTimeout(() => resolve("العملية 2 انتهت"), 500); }); Promise.race([promise1, promise2]) .then(result => { console.log(result); // سيعرض: العملية 2 انتهت }) .catch(error => { console.error("حدث خطأ:", error); });في هذا المثال، على الرغم من أن
promise1تستغرق وقتًا أطول، فإننا نحصل على النتيجة منpromise2لأنها تنتهي أولاً.
الختام
تعد سلسلة الوعود (Promises Chaining) من أبرز الأساليب في جافا سكربت لمعالجة العمليات غير المتزامنة. من خلال استخدام هذه التقنية، يمكن تنظيم الكود بشكل أكثر فعالية، مما يؤدي إلى تقليل التداخل وتحسين قابلية الصيانة. بالإضافة إلى ذلك، توفر الوعود مجموعة من الأدوات المفيدة مثل Promise.all() و Promise.race() التي يمكن استخدامها في سيناريوهات متعددة لتحسين أداء التطبيق وتنظيم العمليات غير المتزامنة بشكل مرن.

