واجهة الوعود البرمجية (Promise API) في جافاسكربت
تعد واجهة الوعود البرمجية (Promise API) في جافاسكربت من أهم المفاهيم التي ظهرت لتسهيل التعامل مع العمليات غير المتزامنة (Asynchronous Operations). قبل ظهور الوعود (Promises)، كان التعامل مع العمليات غير المتزامنة في جافاسكربت يتطلب استخدام التوابع العكسية (Callbacks)، والتي كانت تؤدي في كثير من الأحيان إلى ما يعرف بـ “الجحيم الراجع” (Callback Hell)، حيث يزداد تعقيد الكود كلما زادت العمليات غير المتزامنة، مما جعل قراءة الكود وفهمه أمرًا صعبًا.
مع إدخال واجهة الوعود (Promises) في ECMAScript 6، أصبح من الممكن كتابة كود أكثر تنظيماً، وأكثر قابلية للصيانة والتوسع. في هذا المقال، سنتناول بشكل مفصل مفهوم الوعود في جافاسكربت، كيفية استخدامها، وفهم الآلية التي تقف وراءها.
ما هي الوعود (Promises)؟
الوعود (Promises) هي آلية تمثل عملية غير متزامنة، وهي عبارة عن كائن (Object) يضمن إتمام المهمة في المستقبل، إما بالنجاح أو بالفشل. بشكل بسيط، يمكننا التفكير في الوعد كأنها “التزام” يحدث في المستقبل. هذا الالتزام يتألف من ثلاث حالات رئيسية:
-
الانتظار (Pending): الحالة التي تبدأ بها الوعود عندما يتم إنشاؤها، أي عندما تكون العملية غير متزامنة قيد التنفيذ.
-
النجاح (Fulfilled): عندما تكتمل العملية بنجاح ويعود الكود بالنتيجة أو القيمة المتوقعة.
-
الفشل (Rejected): عندما تفشل العملية غير المتزامنة وتحدث مشكلة تؤدي إلى فشل العملية، مثل حدوث خطأ أو استثناء.
الوعود تساعد في فصل منطق الأعمال عن التعامل مع الأخطاء، مما يجعل الكود أكثر وضوحًا وقابلية للتوسع.
كيفية إنشاء وعد (Promise)
لإنشاء وعد في جافاسكربت، نستخدم البنية الأساسية التي توفرها جافاسكربت وهي new Promise(). تتطلب هذه البنية تمرير دالة التنفيذ (executor) التي تحتوي على منطق التعامل مع العملية غير المتزامنة. الدالة التنفيذية تتلقى معلمتين: resolve و reject، حيث تقوم الدالة resolve بإرجاع القيمة التي تم إنجاز العملية بها في حالة النجاح، بينما تقوم الدالة reject بإرجاع السبب الذي أدى إلى الفشل في حالة حدوث خطأ.
إليك المثال التالي:
javascriptlet promise = new Promise(function(resolve, reject) {
let success = true; // هنا يمكن أن تكون نتيجة أي عملية غير متزامنة مثل تحميل بيانات
if(success) {
resolve("العملية تمت بنجاح!");
} else {
reject("حدث خطأ أثناء العملية.");
}
});
في المثال أعلاه، قمنا بإنشاء وعد يقوم بإرجاع “العملية تمت بنجاح!” إذا كانت المتغيرات تدل على النجاح، أو “حدث خطأ أثناء العملية.” في حالة الفشل.
التعامل مع الوعود (Handling Promises)
لتعامل مع الوعود، يمكننا استخدام الدوال .then() و .catch() لتحديد ما يجب فعله عند نجاح أو فشل الوعود. الدالة .then() تقوم بمعالجة الحالة عند نجاح الوعد، بينما الدالة .catch() تقوم بمعالجة الأخطاء عند فشل الوعد.
إليك مثالاً على كيفية التعامل مع الوعد:
javascriptpromise.then(function(result) {
console.log(result); // سيطبع "العملية تمت بنجاح!"
}).catch(function(error) {
console.log(error); // سيطبع "حدث خطأ أثناء العملية."
});
سلاسل الوعود (Chaining Promises)
من أبرز مميزات الوعود هي إمكانية سلسلة الوعود (Chaining)، أي أنه يمكننا استخدام .then() على الوعد المرتجع من دالة .then() أخرى، مما يتيح لنا تنفيذ عمليات متعددة بشكل تسلسلي.
في هذا السياق، في حالة ما إذا كان لدينا عملية غير متزامنة واحدة تلي الأخرى، يمكننا ربطها في سلسلة باستخدام then().
javascriptlet promise1 = new Promise(function(resolve, reject) {
resolve("العملية الأولى تمت بنجاح!");
});
let promise2 = new Promise(function(resolve, reject) {
resolve("العملية الثانية تمت بنجاح!");
});
promise1.then(function(result) {
console.log(result); // سيطبع "العملية الأولى تمت بنجاح!"
return promise2; // العودة بالوعد الثاني
}).then(function(result) {
console.log(result); // سيطبع "العملية الثانية تمت بنجاح!"
});
التعامل مع عدة وعود في نفس الوقت (Promise.all)
أحد أكثر الاستخدامات شيوعًا للوعود هو التعامل مع عدة وعود غير متزامنة في نفس الوقت. تستخدم الدالة Promise.all() لتجميع عدة وعود معًا والانتظار حتى تنتهي جميع العمليات غير المتزامنة.
إذا كانت كل الوعود ناجحة، سيتم إرجاع مصفوفة تحتوي على نتائج جميع الوعود. أما إذا كان هناك وعد واحد فقط فشل، سيتم رفض جميع الوعود.
javascriptlet promise1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 1000, 'العملية 1');
});
let promise2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 2000, 'العملية 2');
});
Promise.all([promise1, promise2]).then(function(results) {
console.log(results); // سيطبع ["العملية 1", "العملية 2"]
}).catch(function(error) {
console.log("حدث خطأ: " + error);
});
التعامل مع أول وعد يتم (Promise.race)
عند الحاجة لمعالجة أول وعد يتم (سواء كان ناجحًا أو فاشلاً)، يمكننا استخدام الدالة Promise.race(). تقوم هذه الدالة بتحديد أول وعد ينتهي، سواء كان بنجاح أو فشل، وإرجاعه.
javascriptlet promise1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, 'العملية 1');
});
let promise2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'العملية 2');
});
Promise.race([promise1, promise2]).then(function(result) {
console.log(result); // سيطبع "العملية 2" لأن وعد العملية الثانية انتهى أولاً
});
استخدام async و await مع الوعود
تم تقديم الكلمة المفتاحية async و await في ECMAScript 8 (ES8) لتسهيل التعامل مع الوعود وجعل الكود أكثر وضوحًا، حيث يمكن للمبرمج كتابة الكود كما لو كان كودًا متزامنًا، على الرغم من أنه يتعامل مع عمليات غير متزامنة.
-
async: يتم استخدامها قبل دالة للإشارة إلى أنها دالة غير متزامنة. -
await: تستخدم داخل الدالة غير المتزامنة لانتظار الوعد حتى يكتمل قبل متابعة التنفيذ.
javascriptasync function fetchData() {
let promise1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, "تم تحميل البيانات");
});
let result = await promise1;
console.log(result); // سيطبع "تم تحميل البيانات"
}
fetchData();
كيف تعمل الوعود في الخلفية؟
الوعود في جافاسكربت تعمل في دورة الحدث (Event Loop)، التي هي جزء من آلية التنفيذ في جافاسكربت. عندما يتم إنشاء وعد، يتم تنفيذ دالة التنفيذ في وقت لاحق من دورة الحدث بعد إتمام كافة العمليات المتزامنة.
عندما يتم حل الوعد، يتم وضعه في قائمة المهام (Task Queue) أو قائمة الميكرو-تاسك (Microtask Queue) ليتم معالجته في الوقت المناسب.
الوعود في جافاسكربت: مزايا وعيوب
المزايا:
-
تحسين قراءة الكود وصيانته: بدلاً من الاعتماد على التوابع العكسية المتداخلة، توفر الوعود طريقة أكثر وضوحًا لتنظيم الكود.
-
دعم العمليات غير المتزامنة بسهولة: تسهل الوعود التعامل مع العمليات غير المتزامنة المعقدة، مثل طلبات الشبكة أو قراءة الملفات.
-
قابلية التحسين والتوسع: توفر الوعود إمكانية التعامل مع عدة عمليات غير متزامنة بشكل منظم وسلس.
العيوب:
-
قد تكون معقدة: في حالة عدم فهم آلية عمل الوعود، قد تصبح الوعود معقدة وقد تتداخل مع معالجات أخرى.
-
التعامل مع الأخطاء: بالرغم من أنها تحسن من التعامل مع الأخطاء، إلا أن التعامل مع الأخطاء في الوعود قد يكون صعبًا في بعض الحالات إذا كانت العمليات غير المتزامنة متعددة.
الخاتمة
تعد الوعود في جافاسكربت أداة قوية لتسهيل التعامل مع العمليات غير المتزامنة، مما يجعل الكود أكثر تنظيمًا ووضوحًا. من

