بقلم روزاريو دي كيارا✏️
في JavaScript، تعد الوعود أداة فعالة للتعامل مع العمليات غير المتزامنة، وهي مفيدة بشكل خاص في الأحداث المتعلقة بواجهة المستخدم. إنها تمثل قيمة قد لا تكون متاحة على الفور ولكن سيتم حلها في وقت ما في المستقبل.
تسمح الوعود (أو ينبغي أن تسمح) للمطورين بكتابة تعليمات برمجية أكثر وضوحًا وأكثر قابلية للإدارة عند التعامل مع مهام مثل مكالمات واجهة برمجة التطبيقات (API) أو تفاعلات المستخدم أو الرسوم المتحركة. باستخدام أساليب مثل .then() و.catch() و.finally()، تتيح الوعود طريقة أكثر سهولة للتعامل مع سيناريوهات النجاح والخطأ، وتجنب "جحيم رد الاتصال" سيئ السمعة.
في هذه المقالة، سنستخدم الطريقة الجديدة (مارس 2024 Promise.withResolvers()) التي تسمح لك بكتابة تعليمات برمجية أكثر وضوحًا وبساطة عن طريق إرجاع كائن يحتوي على ثلاثة أشياء: Promise جديد ووظيفتين، واحدة لحل الوعد والآخر لرفضه نظرًا لأن هذا تحديث حديث، فستحتاج إلى وقت تشغيل حديث للعقدة (v>22) لتنفيذ الأمثلة الواردة في هذه المقالة.
في قطعتي التعليمات البرمجية المكافئتين وظيفيًا التاليتين، يمكننا مقارنة الطريقة القديمة والطريقة الجديدة لتعيين الطريقة إما لحل الوعد أو رفضه:
let resolve, reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); Math.random() > 0.5 ? resolve("ok") : reject("not ok");
في الكود أعلاه، يمكنك رؤية الاستخدام الأكثر تقليدية للوعد: يمكنك إنشاء كائن وعد جديد، وبعد ذلك، في المُنشئ، يتعين عليك تعيين الوظيفتين، الحل والرفض، اللتين سيتم استدعاؤهما عندما ضروري.
في مقتطف التعليمات البرمجية التالي، تمت إعادة كتابة نفس الجزء من التعليمات البرمجية باستخدام طريقة Promise.withResolvers() الجديدة، ويبدو الأمر أبسط:
const { promise, resolve, reject } = Promise.withResolvers(); Math.random() > 0.5 ? resolve("ok") : reject("not ok");
هنا يمكنك رؤية كيفية عمل النهج الجديد. تقوم بإرجاع Promise، والذي يمكنك من خلاله استدعاء الأسلوب .then() والوظيفتين، الحل والرفض.
يتضمن النهج التقليدي للوعود منطق الإنشاء ومعالجة الأحداث ضمن وظيفة واحدة، مما قد يكون مقيدًا إذا كانت هناك شروط متعددة أو أجزاء مختلفة من التعليمات البرمجية تحتاج إلى حل الوعد أو رفضه.
في المقابل، يوفر Promise.withResolvers() مرونة أكبر من خلال فصل إنشاء Promise عن منطق الحل، مما يجعله مناسبًا لإدارة الظروف المعقدة أو الأحداث المتعددة. ومع ذلك، بالنسبة لحالات الاستخدام المباشرة، قد تكون الطريقة التقليدية أبسط وأكثر دراية لأولئك الذين اعتادوا على أنماط الوعد القياسية.
يمكننا الآن اختبار النهج الجديد بمثال أكثر واقعية. في الكود أدناه، يمكنك رؤية مثال بسيط لاستدعاء واجهة برمجة التطبيقات:
function fetchData(url) { return new Promise((resolve, reject) => { fetch(url) .then(response => { // Check if the response is okay (status 200-299) if (response.ok) { return response.json(); // Parse JSON if response is okay } else { // Reject the promise if the response is not okay reject(new Error('API Invocation failed')); } }) .then(data => { // Resolve the promise with the data resolve(data); }) .catch(error => { // Catch and reject the promise if there is a network error reject(error); }); }); } // Example usage const apiURL = ''; fetchData(apiURL) .then(data => { // Handle the resolved data console.log('Data received:', data); }) .catch(error => { // Handle any errors that occurred console.error('Error occurred:', error); });
تم تصميم وظيفة fetchData للحصول على عنوان URL وإرجاع وعد يتعامل مع استدعاء واجهة برمجة التطبيقات (API) باستخدام واجهة برمجة تطبيقات الجلب. يقوم بمعالجة الاستجابة عن طريق التحقق مما إذا كانت حالة الاستجابة ضمن نطاق 200-299، مما يشير إلى النجاح.
في حالة النجاح، يتم تحليل الاستجابة بتنسيق JSON، ويتم حل الوعد بالبيانات الناتجة. إذا لم تكن الاستجابة ناجحة، فسيتم رفض الوعد مع ظهور رسالة خطأ مناسبة. بالإضافة إلى ذلك، تتضمن الوظيفة معالجة الأخطاء لاكتشاف أي أخطاء في الشبكة، ورفض الوعد في حالة حدوث مثل هذا الخطأ.
يوضح المثال كيفية استخدام هذه الوظيفة، ويوضح كيفية إدارة البيانات التي تم حلها باستخدام كتلة .then() والتعامل مع الأخطاء باستخدام كتلة .catch()، مما يضمن إدارة كل من عملية استرداد البيانات الناجحة والأخطاء بشكل مناسب.
في الكود أدناه، نعيد كتابة الدالة fetchData() باستخدام طريقة Promise.withResolvers() الجديدة:
function fetchData(url) { const { promise, resolve, reject } = Promise.withResolvers(); fetch(url) .then(response => { // Check if the response is okay (status 200-299) if (response.ok) { return response.json(); // Parse JSON if response is okay } else { // Reject the promise if the response is not okay reject(new Error('API Invocation failed')); } }) .then(data => { // Resolve the promise with the data resolve(data); }) .catch(error => { // Catch and reject the promise if there is a network error reject(error); }); return promise; }
كما ترون، الكود أعلاه أكثر قابلية للقراءة، ودور الكائن Promise واضح: ستعيد وظيفة fetchData وعدًا سيتم حله بنجاح أو سيفشل، مع استدعاء - في كل حالة - الطريقة المناسبة . يمكنك العثور على الكود أعلاه في المستودع المسمى api.invocation.{old|new}.js.
يستكشف الكود التالي كيفية تنفيذ طريقة إلغاء الوعد. كما تعلم، لا يمكنك إلغاء الوعد في JavaScript. تمثل الوعود نتيجة عملية غير متزامنة وهي مصممة لحلها أو رفضها بمجرد إنشائها، مع عدم وجود آلية مدمجة لإلغائها.
ينشأ هذا القيد لأن الوعود لها عملية انتقال حالة محددة؛ فهي تبدأ كمعلقة، وبمجرد تسويتها، لا يمكنها تغيير الحالة. والمقصود منها هو تغليف نتيجة العملية بدلاً من التحكم في العملية نفسها، مما يعني أنها لا تستطيع التأثير على العملية الأساسية أو إلغائها. إن اختيار التصميم هذا يجعل الوعود بسيطة وتركز على تمثيل النتيجة النهائية للعملية:
const cancellablePromise = () => { const { promise, resolve, reject } = Promise.withResolvers(); promise.cancel = () => { reject("the promise got cancelled"); }; return promise; };
في الكود أعلاه، يمكنك رؤية الكائن المسمى CancelablePromise، وهو وعد باستخدام طريقة Cancel() إضافية والتي، كما ترون، تفرض ببساطة استدعاء طريقة الرفض. هذا مجرد سكر نحوي ولا يلغي وعد جافا سكريبت، على الرغم من أنه قد يساعد في كتابة تعليمات برمجية أكثر وضوحًا.
هناك طريقة بديلة تتمثل في استخدام AbortController وAbortSignal، والتي يمكن ربطها بالعملية الأساسية (على سبيل المثال، طلب HTTP) لإلغائها عند الحاجة. من الوثائق، يمكنك أن ترى أن نهج AbortController وAbortSignal هو تطبيق أكثر تعبيرًا لما قمنا بتنفيذه في الكود أعلاه: بمجرد استدعاء AbortSignal، يتم رفض الوعد.
هناك طريقة أخرى تتمثل في استخدام مكتبات البرمجة التفاعلية مثل RxJS، والتي توفر تنفيذًا للنمط القابل للملاحظة، وتحكمًا أكثر تعقيدًا في تدفقات البيانات غير المتزامنة، بما في ذلك إمكانات الإلغاء.
عند الحديث عن حالات الاستخدام العملي، تعد الوعود مناسبة تمامًا للتعامل مع العمليات الفردية غير المتزامنة، مثل جلب البيانات من واجهة برمجة التطبيقات. في المقابل، تعتبر Observables مثالية لإدارة تدفقات البيانات، مثل إدخال المستخدم أو أحداث WebSocket أو استجابات HTTP، حيث قد يتم إصدار قيم متعددة بمرور الوقت.
لقد أوضحنا بالفعل أنه بمجرد البدء، لا يمكن إلغاء الوعود، في حين تسمح العناصر القابلة للملاحظة بالإلغاء عن طريق إلغاء الاشتراك من البث. الفكرة العامة هي أنه مع العناصر القابلة للملاحظة، لديك بنية واضحة للتفاعل المحتمل مع الكائن:
ويظهر ذلك في الكود أدناه:
import { Observable } from 'rxjs'; const observable = new Observable(subscriber => { subscriber.next(1); subscriber.next(2); subscriber.next(3); subscriber.complete(); }); const observer = observable.subscribe({ next(x) { console.log('Received value:', x); }, complete() { console.log('Observable completed'); } }); observer.unsubscribe();
لا يمكن إعادة كتابة هذا الرمز باستخدام الوعود لأن Observable يُرجع ثلاث قيم بينما لا يمكن حل الوعد إلا مرة واحدة.
لمزيد من التجربة باستخدام طريقة إلغاء الاشتراك، يمكننا إضافة مراقب آخر يستخدم طريقة takeWhile(): سيسمح للمراقب بالانتظار حتى تتطابق القيم مع شرط معين؛ في الكود أدناه، على سبيل المثال، يستمر في تلقي الأحداث من Observable بينما القيمة ليست 2:
import { Observable, takeWhile } from 'rxjs'; const observable = new Observable(subscriber => { subscriber.next(1); subscriber.next(2); subscriber.next(3); subscriber.complete(); }); const observer1 = observable.subscribe({ next(x) { console.log('Received by 1 value:', x); }, complete() { console.log('Observable 1 completed'); } }); const observer2 = observable.pipe( takeWhile(value => value != "2") ).subscribe(value => console.log('Received by 2 value:', value));
في الكود أعلاه، Observer1 هو نفسه الذي رأيناه بالفعل: سوف يشترك فقط ويستمر في تلقي جميع الأحداث من Observable. أما العنصر الثاني، المراقب 2، فسوف يتلقى عناصر من العناصر القابلة للملاحظة أثناء مطابقة الشرط. في هذه الحالة، هذا يعني عندما تكون القيمة مختلفة عن 2.
من التنفيذ، يمكنك رؤية كيفية عمل الآليتين المختلفتين:
$ node observable.mjs Received by 1 value: 1 Received by 1 value: 2 Received by 1 value: 3 Observable 1 completed Received by 2 value: 1 $
في هذه المقالة، قمنا بالتحقق من الآلية الجديدة لتخصيص الوعد في JavaScript ووضعنا بعض الطرق الممكنة لإلغاء الوعد قبل اكتماله. قمنا أيضًا بمقارنة الوعود بالكائنات التي يمكن ملاحظتها، والتي لا تقدم ميزات الوعود فحسب، بل تعمل على توسيعها من خلال السماح بإصدارات متعددة للأحداث وآلية مناسبة لإلغاء الاشتراك.
يعد تصحيح الأخطاء دائمًا مهمة شاقة. ولكن كلما فهمت أخطائك أكثر، أصبح من الأسهل إصلاحها.
يتيح لك LogRocket فهم هذه الأخطاء بطرق جديدة وفريدة من نوعها. يعمل حل مراقبة الواجهة الأمامية الخاص بنا على تتبع تفاعل المستخدم مع واجهات JavaScript الأمامية لديك ليمنحك القدرة على رؤية ما فعله المستخدم بالضبط وأدى إلى حدوث خطأ.
يسجل LogRocket سجلات وحدة التحكم، وأوقات تحميل الصفحة، وتتبعات المكدس، وطلبات/استجابات الشبكة البطيئة مع نصوص الرؤوس، وبيانات تعريف المتصفح، والسجلات المخصصة. إن فهم تأثير كود JavaScript الخاص بك لن يكون أسهل من أي وقت مضى!
جربه مجانًا.
تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.
Copyright© 2022 湘ICP备2022001581号-3