تحويل الدوال إلى وعود (Promisification) هي عملية تغليف الدالة التي تستلم ردّ نداء لتصبح دالة تُعيد وعدًا.
وفي الحياة العملية فهذا النوع من التحويل مطلوب جدًا إذ تعتمد العديد من الدوال والمكتبات على ردود النداء. ولكن… الوعود أسهل وأفضل لذا من المنطقي تحويل تلك الدوال.
لنأخذ مثلًا دالة loadScript(src, callback)
من الفصل مقدمة إلى ردود النداء callback:
function loadScript(src, callback) { let script = document.createElement('script'); script.src = src; script.onload = () => callback(null, script); script.onerror = () => callback(new Error(`Script load error for ${src}`)); document.head.append(script); } // الاستعمال // loadScript('path/script.js', (err, script) => {...})
هيا نحوّلها. على الدالة الجديدة loadScriptPromise(src)
القيام بنفس ما تقوم به تلك، ولكن لا تقبل إلّا src
وسيطًا (بدون callback
) وتُعيد وعدًا.
let loadScriptPromise = function(src) { return new Promise((resolve, reject) => { loadScript(src, (err, script) => { if (err) reject(err) else resolve(script); }); }) } // الاستعمال // loadScriptPromise('path/script.js').then(...)
الآن يمكننا استعمال loadScriptPromise
بسهولة بالغة في الشيفرات التي تعتمد الوعود.
وكما نرى فالدالة تكلّف الدالة الأصلية loadScript
بكلّ العمل اللازم، وما تفعله هو تقديم ردّ نداء من عندها تحوّله إلى وعد resolve/reject
.
نحتاج عمليًا إلى تحويل دوال عديدة وكثيرة لتعتمد الوعود، لذا من المنطقي استعمال دالة مساعِدة.
لنسمّها promisify(f)
وستقبل دالة الأصل f
اللازم تحويلها، وتُعيد دالة غالِفة.
يؤدّي الغلاف نفس عمل الشيفرة أعلاه: يُعيد وعدًا ويمرّر النداء إلى الدالة الأصلية f
متتّبعًا الناتج في ردّ نداء يصنعه بنفسه:
function promisify(f) { return function (...args) { // يعيد التابع المغلّف return new Promise((resolve, reject) => { function callback(err, result) { // رد النداء خاصتنا لـِ f if (err) { return reject(err); } else { resolve(result); } } args.push(callback); // نضيف رد النداء خاصتنا إلى نهاية واسطاء f f.call(this, ...args); // استدعي التابع الأصلي }); }; }; // طريقة الاستخدام: let loadScriptPromise = promisify(loadScript); loadScriptPromise(...).then(...);
نفترض هنا بأنّ الدالة الأصلية تتوقّع استلام ردّ نداء له وسيطين (err, result)
، وهذا ما نواجهه أغلب الوقت لهذا كتبنا ردّ النداء المخصّص بهذا التنسيق، وتعمل دالة promisify
على أكمل وجه… لهذه الحالات.
ولكن ماذا لو كانت تتوقّع f
ردّ نداء له وسطاء أكثر callback(err, res1, res2, ...)
؟
إليك نسخة promisify
ذكية محسّنة: لو استدعيناها هكذا promisify(f, true)
فسيكون ناتج الوعد مصفوفة من نواتج ردود النداء [res1, res2, ...]
:
// التابع promisify(f, true) لجب مصفوفة النتائج function promisify(f, manyArgs = false) { return function (...args) { return new Promise((resolve, reject) => { function callback(err, ...results) { // رد النداء خاصتنا لـِ f if (err) { return reject(err); } else { // إجلب جميع ردود النداء وإذا حدّد أكثر من وسيط resolve(manyArgs ? results : results[0]); } } args.push(callback); f.call(this, ...args); }); }; }; // طريقة الاستخدام: f = promisify(f, true); f(...).then(arrayOfResults => ..., err => ...)
أمّا لتنسيقات ردود النداء الشاذّة (مثل التي بدون وسيط err
أصلًا callback(result)
) فيمكننا تحويلها يدويًا بدون استعمال الدالة المساعِدة.
كما وهناك وحدات لها دوال تحويل مرنة أكثر في التعامل مثل es6-promisify. وفي Node.js نرى الدالة المضمّنة util.promisify
لهذا الغرض.
ملاحظة: يعدّ تحويل الدوال إلى وعود نهجًا رائعًا، خاصةً عند استخدام async/await
(الّتي سنراها في الفصل التالي)، ولكن ليس بديلًا كليًا لردود النداء.
تذكر أن الوعد له نتيجة واحدة فقط، ولكن تقنيًا ممكن أن تُستدعى ردود النداء عدة مرات.
لذا فإن تحويل الدوال إلى وعود مخصصة للدوال التي تستدعي ردود النداء لمرة واحدة. وستُتجاهل جميع الاستدعاءات اللاحقة.
ترجمة -وبتصرف- للفصل Promisification من كتاب The JavaScript language
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.