إدارة المهام طويلة المدى في تطبيق React مع عمال الويب
نشرت: 2022-03-10يعتبر وقت الاستجابة مشكلة كبيرة عندما يتعلق الأمر بتطبيقات الويب. يطلب المستخدمون ردودًا فورية ، بغض النظر عما يفعله تطبيقك. سواء كان الأمر يتعلق فقط بعرض اسم الشخص أو تحليل الأرقام ، فإن مستخدمو تطبيق الويب يطالبون بأن يستجيب تطبيقك لأمرهم في كل مرة. في بعض الأحيان قد يكون من الصعب تحقيق ذلك نظرًا لطبيعة الخيوط الواحدة لجافا سكريبت. ولكن في هذه المقالة ، سنتعلم كيف يمكننا الاستفادة من Web Worker API لتقديم تجربة أفضل.
في كتابة هذا المقال ، وضعت الافتراضات التالية:
- لتكون قادرًا على المتابعة ، يجب أن يكون لديك على الأقل بعض الإلمام بجافا سكريبت وواجهة برمجة التطبيقات للمستند ؛
- يجب أن يكون لديك أيضًا معرفة عملية بـ React حتى تتمكن من بدء مشروع React جديد بنجاح باستخدام تطبيق Create React.
إذا كنت بحاجة إلى مزيد من الأفكار حول هذا الموضوع ، فقد قمت بتضمين عدد من الروابط في قسم "موارد إضافية" لمساعدتك في الحصول على السرعة.
أولاً ، لنبدأ مع Web Workers.
ما هو عامل الويب؟
لفهم عمال الويب والمشكلة التي من المفترض حلها ، من الضروري فهم كيفية تنفيذ تعليمات JavaScript البرمجية في وقت التشغيل. أثناء وقت التشغيل ، يتم تنفيذ تعليمات JavaScript البرمجية بالتتابع وبطريقة خطوة بخطوة. بمجرد انتهاء جزء من التعليمات البرمجية ، يبدأ تشغيل الجزء التالي في السطر ، وهكذا. من الناحية الفنية ، نقول أن JavaScript هو خيط واحد. يشير هذا السلوك إلى أنه بمجرد بدء تشغيل جزء من التعليمات البرمجية ، يجب أن ينتظر كل رمز يأتي بعد ذلك الرمز حتى ينتهي التنفيذ. وبالتالي ، فإن كل سطر من التعليمات البرمجية "يمنع" تنفيذ أي شيء آخر يأتي بعده. لذلك من المستحسن أن يتم إنهاء كل جزء من التعليمات البرمجية بأسرع ما يمكن. إذا استغرق جزء من التعليمات البرمجية وقتًا طويلاً لإنهاء برنامجنا ، فقد يبدو أنه توقف عن العمل. على المتصفح ، يظهر هذا كصفحة مجمدة غير مستجيبة. في بعض الحالات القصوى ، ستتجمد علامة التبويب تمامًا.
تخيل القيادة في حارة واحدة. إذا توقف أي من السائقين أمامك عن الحركة لأي سبب من الأسباب ، فأنت تعاني من ازدحام مروري. باستخدام برنامج مثل Java ، يمكن أن تستمر حركة المرور في الممرات الأخرى. وهكذا يقال أن Java متعددة الخيوط. يعد عمال الويب محاولة لجلب السلوك متعدد الخيوط إلى JavaScript.
توضح لقطة الشاشة أدناه أن Web Worker API مدعوم من قبل العديد من المتصفحات ، لذلك يجب أن تشعر بالثقة في استخدامها.

يعمل Web Workers في مؤشرات الترابط في الخلفية دون التداخل مع واجهة المستخدم ، ويتواصلون مع الكود الذي أنشأهم عن طريق معالجات الأحداث.
يأتي التعريف الممتاز لعامل الويب من MDN:
"العامل هو كائن تم إنشاؤه باستخدام مُنشئ (على سبيل المثالWorker()
يقوم بتشغيل ملف JavaScript مسمى - يحتوي هذا الملف على الكود الذي سيتم تشغيله في مؤشر ترابط العامل ؛ يعمل العمال في سياق عام آخر يختلف عنwindow
الحالية. وهكذا ، باستخدام اختصارwindow
للحصول على النطاق العام الحالي (بدلاً منself
داخلWorker
سيعرض خطأ. "
يتم إنشاء Worker
باستخدام المنشئ العامل.
const worker = new Worker('worker-file.js')
من الممكن تشغيل معظم التعليمات البرمجية داخل عامل الويب ، مع بعض الاستثناءات. على سبيل المثال ، لا يمكنك معالجة DOM من داخل العامل. لا يوجد وصول إلى document
API.
يرسل العمال والموضوع الذي يولدهم رسائل إلى بعضهم البعض باستخدام طريقة postMessage()
. وبالمثل ، فإنهم يستجيبون للرسائل باستخدام معالج أحداث onmessage
. من المهم أن تحصل على هذا الاختلاف. يتم إرسال الرسائل باستخدام طريقة ؛ يتطلب تلقي رسالة مرة أخرى معالج حدث. يتم تضمين الرسالة التي يتم تلقيها في سمة data
الحدث. سنرى مثالاً على ذلك في القسم التالي. لكن اسمحوا لي أن أذكر بسرعة أن نوع العامل الذي كنا نناقشه يسمى "العامل المتفاني". هذا يعني أن العامل يمكن الوصول إليه فقط من خلال البرنامج النصي الذي يطلق عليه. من الممكن أيضًا أن يكون لديك عامل يمكن الوصول إليه من خلال نصوص متعددة. تسمى هذه بالعاملين المشتركين ويتم إنشاؤها باستخدام مُنشئ SharedWorker
، كما هو موضح أدناه.
const sWorker = new SharedWorker('shared-worker-file.js')
لمعرفة المزيد حول العمال ، يرجى الاطلاع على مقال MDN هذا. الغرض من هذه المقالة هو البدء في استخدام العاملين على الويب. دعنا نصل إليها بحساب رقم فيبوناتشي التاسع.
حساب رقم فيبوناتشي Nth
ملاحظة: بالنسبة لهذا القسمين التاليين ، أستخدم Live Server على VSCode لتشغيل التطبيق. يمكنك بالتأكيد استخدام شيء آخر.
هذا هو القسم الذي كنت تنتظره. سنقوم أخيرًا بكتابة بعض التعليمات البرمجية لرؤية Web Workers أثناء العمل. حسنًا ، ليس بهذه السرعة. لن نقدر الوظيفة التي يقوم بها Web Worker إلا إذا واجهنا نوع المشكلات التي يحلها. في هذا القسم ، سنرى مثالاً لمشكلة ، وفي القسم التالي ، سنرى كيف يساعدنا عامل الويب في تحسين أدائنا.
تخيل أنك كنت تبني تطبيق ويب يسمح للمستخدمين بحساب رقم فيبوناتشي التاسع. إذا كنت جديدًا على مصطلح "رقم فيبوناتشي" ، يمكنك قراءة المزيد عنه هنا ، ولكن باختصار ، فإن أرقام فيبوناتشي هي سلسلة من الأرقام بحيث يكون كل رقم هو مجموع العددين السابقين.
رياضيا ، يتم التعبير عنها على النحو التالي:

وبالتالي فإن الأرقام القليلة الأولى من التسلسل هي:
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 ...
في بعض المصادر ، يبدأ التسلسل عند F 0 = 0
، وفي هذه الحالة تبقى الصيغة أدناه صحيحة لـ n > 1
:

في هذه المقالة سنبدأ من F 1 = 1. شيء واحد يمكننا رؤيته على الفور من الصيغة هو أن الأرقام تتبع نمطًا تعاوديًا. المهمة المطروحة الآن هي كتابة دالة تكرارية لحساب رقم فيبوناتشي التاسع (FN).
بعد عدة محاولات ، أعتقد أنه يمكنك بسهولة التوصل إلى الوظيفة أدناه.
const fib = n => { if (n < 2) { return n // or 1 } else { return fib(n - 1) + fib(n - 2) } }
الوظيفة بسيطة. إذا كانت n أقل من 2 ، فقم بإرجاع n (أو 1) ، وإلا فقم بإرجاع مجموع n-1
و n-2
FNs. مع وظائف السهم والمعامل الثلاثي ، يمكننا التوصل إلى خط واحد.
const fib = n => (n < 2 ? n : fib(n-1) + fib(n-2))
هذه الوظيفة لها تعقيد زمني 0(2 n )
. هذا يعني ببساطة أنه مع زيادة قيمة n ، يزداد الوقت المطلوب لحساب المجموع أضعافًا مضاعفة. هذا يجعل مهمة طويلة الأمد حقًا يمكن أن تتداخل مع واجهة المستخدم الخاصة بنا ، لقيم كبيرة لـ n. دعونا نرى هذا في العمل.
ملاحظة : هذه ليست بأي حال أفضل طريقة لحل هذه المشكلة بالذات. خياري لاستخدام هذه الطريقة هو لغرض هذه المقالة.
للبدء ، قم بإنشاء مجلد جديد وقم بتسميته كما تريد. الآن داخل هذا المجلد ، قم بإنشاء مجلد src/
. أيضًا ، قم بإنشاء ملف index.html
في المجلد الجذر. داخل المجلد src/
، أنشئ ملفًا باسم index.js
.
افتح index.html
وأضف كود HTML التالي.
<!DOCTYPE html> <html> <head> <link rel="stylesheet" href="styles.css"> </head> <body> <div class="heading-container"> <h1>Computing the nth Fibonnaci number</h1> </div> <div class="body-container"> <p id='error' class="error"></p> <div class="input-div"> <input id='number-input' class="number-input" type='number' placeholder="Enter a number" /> <button id='submit-btn' class="btn-submit">Calculate</button> </div> <div id='results-container' class="results"></div> </div> <script src="/src/index.js"></script> </body> </html>
هذا الجزء بسيط جدا. أولا ، لدينا عنوان. ثم لدينا حاوية مع إدخال وزر. يقوم المستخدم بإدخال رقم ثم النقر فوق "حساب". لدينا أيضًا حاوية للاحتفاظ بنتيجة الحساب. أخيرًا ، نقوم بتضمين ملف src/index.js
في علامة script
.
يمكنك حذف رابط ورقة الأنماط. ولكن إذا كان لديك وقت قصير ، فقد حددت بعض CSS التي يمكنك استخدامها. ما عليك سوى إنشاء ملف styles.css
في المجلد الجذر وإضافة الأنماط أدناه:
body { margin: 0; padding: 0; box-sizing: border-box; } .body-container, .heading-container { padding: 0 20px; } .heading-container { padding: 20px; color: white; background: #7a84dd; } .heading-container > h1 { margin: 0; } .body-container { width: 50% } .input-div { margin-top: 15px; margin-bottom: 15px; display: flex; align-items: center; } .results { width: 50vw; } .results>p { font-size: 24px; } .result-div { padding: 5px 10px; border-radius: 5px; margin: 10px 0; background-color: #e09bb7; } .result-div p { margin: 5px; } span.bold { font-weight: bold; } input { font-size: 25px; } p.error { color: red; } .number-input { padding: 7.5px 10px; } .btn-submit { padding: 10px; border-radius: 5px; border: none; background: #07f; font-size: 24px; color: white; cursor: pointer; margin: 0 10px; }
الآن افتح src/index.js
ببطء. أضف الكود أدناه.
const fib = (n) => (n < 2 ? n : fib(n - 1) + fib(n - 2)); const ordinal_suffix = (num) => { // 1st, 2nd, 3rd, 4th, etc. const j = num % 10; const k = num % 100; switch (true) { case j === 1 && k !== 11: return num + "st"; case j === 2 && k !== 12: return num + "nd"; case j === 3 && k !== 13: return num + "rd"; default: return num + "th"; } }; const textCont = (n, fibNum, time) => { const nth = ordinal_suffix(n); return ` <p id='timer'>Time: <span class='bold'>${time} ms</span></p> <p><span class="bold" id='nth'>${nth}</span> fibonnaci number: <span class="bold" id='sum'>${fibNum}</span></p> `; };
هنا لدينا ثلاث وظائف. الأول هو الوظيفة التي رأيناها سابقًا لحساب Nth FN. الوظيفة الثانية هي مجرد وظيفة مساعدة لإرفاق لاحقة مناسبة لرقم صحيح. تأخذ الوظيفة الثالثة بعض الوسائط وتخرج ترميزًا سنقوم بإدراجه لاحقًا في DOM. الوسيطة الأولى هي الرقم الذي يتم حساب FN الخاص به. الحجة الثانية هي FN المحسوبة. الوسيطة الأخيرة هي الوقت المستغرق لإجراء الحساب.
لا يزال في src/index.js
، أضف الكود أدناه أسفل الرمز السابق مباشرةً.
const errPar = document.getElementById("error"); const btn = document.getElementById("submit-btn"); const input = document.getElementById("number-input"); const resultsContainer = document.getElementById("results-container"); btn.addEventListener("click", (e) => { errPar.textContent = ''; const num = window.Number(input.value); if (num < 2) { errPar.textContent = "Please enter a number greater than 2"; return; } const startTime = new Date().getTime(); const sum = fib(num); const time = new Date().getTime() - startTime; const resultDiv = document.createElement("div"); resultDiv.innerHTML = textCont(num, sum, time); resultDiv.className = "result-div"; resultsContainer.appendChild(resultDiv); });
أولاً ، نستخدم document
API للحصول على عقد DOM
في ملف HTML الخاص بنا. نحصل على إشارة إلى الفقرة حيث سنعرض رسائل الخطأ ؛ المدخل؛ زر الحساب والحاوية حيث سنعرض نتائجنا.
بعد ذلك ، نرفق معالج حدث "click" بالزر. عند النقر على الزر ، نأخذ كل ما بداخل عنصر الإدخال ونحوله إلى رقم ، وإذا حصلنا على أي شيء أقل من 2 ، فإننا نعرض رسالة خطأ ونرجع. إذا حصلنا على رقم أكبر من 2 ، نواصل. أولاً ، نسجل الوقت الحالي. بعد ذلك ، نحسب FN. عندما ينتهي ذلك ، نحصل على فارق زمني يمثل المدة التي استغرقها الحساب. في الجزء المتبقي من الكود ، نقوم بإنشاء div
جديد. ثم قمنا بتعيين HTML الداخلي الخاص به ليكون ناتج وظيفة textCont()
التي حددناها سابقًا. أخيرًا ، نضيف فئة إليها (للتصميم) ونلحقها بحاوية النتائج. تأثير هذا هو أن كل حساب سيظهر في div
منفصل أسفل السابق.

يمكننا أن نرى أنه مع زيادة الرقم ، يزداد وقت الحساب أيضًا (أسيًا). على سبيل المثال ، من 30 إلى 35 ، كان لدينا وقت الحساب يقفز من 13 مللي ثانية إلى 130 مللي ثانية. لا يزال بإمكاننا اعتبار هذه العمليات "سريعة". عند سن الأربعين ، نرى وقت حساب يزيد عن ثانية واحدة. على جهازي ، هذا هو المكان الذي أبدأ فيه بملاحظة عدم استجابة الصفحة. في هذه المرحلة ، لم يعد بإمكاني التفاعل مع الصفحة أثناء إجراء الحساب. لا يمكنني التركيز على المدخلات أو القيام بأي شيء آخر.
أتذكر عندما تحدثنا عن JavaScript كونها خيوط واحدة؟ حسنًا ، تم "حظر" هذا الخيط بواسطة هذا الحساب طويل الأمد ، لذلك يجب أن "ينتظر" كل شيء آخر حتى ينتهي. قد يبدأ بقيمة أقل أو أعلى على جهازك ، لكنك ملزم بالوصول إلى هذه النقطة. لاحظ أن الأمر استغرق ما يقرب من 10 ثوانٍ لحساب 44. إذا كانت هناك أشياء أخرى يجب القيام بها على تطبيق الويب الخاص بك ، حسنًا ، يجب على المستخدم الانتظار حتى ينتهي Fib (44) قبل أن يتمكن من المتابعة. ولكن إذا قمت بنشر عامل ويب للتعامل مع هذا الحساب ، فيمكن للمستخدمين متابعة شيء آخر أثناء تشغيل ذلك.
دعنا الآن نرى كيف يساعدنا العاملون على الويب في التغلب على هذه المشكلة.
مثال عامل الويب في العمل
في هذا القسم ، سنفوض مهمة حساب FN رقم لعامل الويب. سيساعد ذلك في تحرير الخيط الرئيسي والحفاظ على استجابة واجهة المستخدم الخاصة بنا أثناء إجراء الحساب.

البدء مع العاملين على الويب بسيط بشكل مدهش. دعونا نرى كيف. أنشئ ملفًا جديدًا src/fib-worker.js
. وأدخل الكود التالي.
const fib = (n) => (n < 2 ? n : fib(n - 1) + fib(n - 2)); onmessage = (e) => { const { num } = e.data; const startTime = new Date().getTime(); const fibNum = fib(num); postMessage({ fibNum, time: new Date().getTime() - startTime, }); };
لاحظ أننا نقلنا الوظيفة التي تحسب رقم فيبوناتشي التاسع ، fib
داخل هذا الملف. سيتم تشغيل هذا الملف بواسطة عامل الويب لدينا.
تذكر في القسم ما هو عامل الويب ، ذكرنا أن العاملين على الويب وأولياء أمورهم يتواصلون باستخدام معالج حدث onmessage
وطريقة postMessage()
. نحن هنا نستخدم معالج حدث onmessage
للاستماع إلى الرسائل من البرنامج النصي الأصلي. بمجرد تلقينا رسالة ، نقوم بتدمير الرقم من سمة البيانات الخاصة بالحدث. بعد ذلك ، نحصل على الوقت الحالي ونبدأ الحساب. بمجرد أن تصبح النتيجة جاهزة ، نستخدم طريقة postMessage()
لنشر النتائج مرة أخرى إلى البرنامج النصي الأصلي.
افتح src/index.js
فلنقم ببعض التغييرات.
... const worker = new window.Worker("src/fib-worker.js"); btn.addEventListener("click", (e) => { errPar.textContent = ""; const num = window.Number(input.value); if (num < 2) { errPar.textContent = "Please enter a number greater than 2"; return; } worker.postMessage({ num }); worker.onerror = (err) => err; worker.onmessage = (e) => { const { time, fibNum } = e.data; const resultDiv = document.createElement("div"); resultDiv.innerHTML = textCont(num, fibNum, time); resultDiv.className = "result-div"; resultsContainer.appendChild(resultDiv); }; });
أول شيء يجب القيام به هو إنشاء عامل الويب باستخدام مُنشئ Worker
. ثم داخل مستمع حدث الزر ، نرسل رقمًا إلى العامل باستخدام worker.postMessage({ num })
. بعد ذلك ، قمنا بتعيين وظيفة للاستماع إلى الأخطاء في العامل. هنا نعيد الخطأ ببساطة. يمكنك بالتأكيد فعل المزيد إذا أردت ، مثل عرضها في DOM. بعد ذلك ، نستمع إلى الرسائل من العامل. بمجرد تلقينا رسالة ، ندمر time
و fibNum
، ونواصل عملية إظهارهما في DOM.
لاحظ أنه داخل عامل الويب ، يتوفر حدث onmessage
في نطاق العامل ، لذا كان بإمكاننا كتابته كـ self.onmessage
و self.postMessage()
. لكن في النص الأصلي ، علينا إرفاق هذه بالعامل نفسه.
في لقطة الشاشة أدناه ، سترى ملف Web worker في علامة تبويب المصادر في Chrome Dev Tools. ما يجب أن تلاحظه هو أن واجهة المستخدم تظل مستجيبة بغض النظر عن الرقم الذي تدخله. هذا السلوك هو سحر العاملين على الويب.

لقد أحرزنا تقدمًا كبيرًا مع تطبيق الويب الخاص بنا. ولكن هناك شيء آخر يمكننا القيام به لتحسينه. يستخدم تطبيقنا الحالي عاملًا واحدًا للتعامل مع كل عملية حسابية. إذا ظهرت رسالة جديدة أثناء تشغيل أحدها ، فسيتم استبدال الرسالة القديمة. للتغلب على هذا ، يمكننا إنشاء عامل جديد لكل مكالمة لحساب FN. دعونا نرى كيفية القيام بذلك في القسم التالي.
العمل مع العديد من العاملين على الويب
حاليًا ، نتعامل مع كل طلب مع عامل واحد. وبالتالي ، سيحل الطلب الوارد محل الطلب السابق الذي لم ينته بعد. ما نريده الآن هو إجراء تغيير بسيط لإنشاء عامل ويب جديد لكل طلب. سنقتل هذا العامل بمجرد الانتهاء.
افتح src/index.js
السطر الذي ينشئ عامل الويب داخل معالج حدث النقر بالزر. الآن يجب أن يبدو معالج الحدث كما يلي.
btn.addEventListener("click", (e) => { errPar.textContent = ""; const num = window.Number(input.value); if (num < 2) { errPar.textContent = "Please enter a number greater than 2"; return; } const worker = new window.Worker("src/fib-worker.js"); // this line has moved inside the event handler worker.postMessage({ num }); worker.onerror = (err) => err; worker.onmessage = (e) => { const { time, fibNum } = e.data; const resultDiv = document.createElement("div"); resultDiv.innerHTML = textCont(num, fibNum, time); resultDiv.className = "result-div"; resultsContainer.appendChild(resultDiv); worker.terminate() // this line terminates the worker }; });
أجرينا تغييرين.
- لقد
const worker = new window.Worker("src/fib-worker.js")
داخل معالج حدث النقر على الزر. - أضفنا هذا العامل.
worker.terminate()
لتجاهل العامل بمجرد الانتهاء من ذلك.
لذلك لكل نقرة على الزر ، نقوم بإنشاء عامل جديد للتعامل مع الحساب. وهكذا يمكننا الاستمرار في تغيير المدخلات ، وستظهر كل نتيجة على الشاشة بمجرد انتهاء الحساب. في لقطة الشاشة أدناه ، يمكنك أن ترى أن قيم 20 و 30 تظهر قبل قيم 45. لكنني بدأت 45 أولاً. بمجرد إرجاع الدالة لـ 20 و 30 ، يتم ترحيل نتائجها ، ويتم إنهاء العامل. عندما ينتهي كل شيء ، لا ينبغي أن يكون لدينا أي عمال في علامة تبويب المصادر.

يمكننا إنهاء هذه المقالة هنا ، ولكن إذا كان هذا تطبيقًا تفاعليًا ، فكيف يمكننا جلب عمال الويب إليه. هذا هو محور القسم التالي.
عمال الويب في رد الفعل
للبدء ، أنشئ تطبيق تفاعل جديد باستخدام CRA. انسخ ملف fib-worker.js
إلى المجلد public/
المجلد الخاص بتطبيق رد الفعل. ينبع وضع الملف هنا من حقيقة أن تطبيقات React هي تطبيقات من صفحة واحدة. هذا يتعلق بالشيء الوحيد المخصص لاستخدام العامل في تطبيق رد الفعل. كل ما يلي من هنا هو رد فعل خالص.
في src/
المجلد ، قم بإنشاء ملف helpers.js
وقم بتصدير وظيفة ordinal_suffix()
منه.
// src/helpers.js export const ordinal_suffix = (num) => { // 1st, 2nd, 3rd, 4th, etc. const j = num % 10; const k = num % 100; switch (true) { case j === 1 && k !== 11: return num + "st"; case j === 2 && k !== 12: return num + "nd"; case j === 3 && k !== 13: return num + "rd"; default: return num + "th"; } };
سيطلب منا تطبيقنا الحفاظ على بعض الحالات ، لذلك قم بإنشاء ملف آخر ، src/reducer.js
ولصقه في مخفض الحالة.
// src/reducers.js export const reducer = (state = {}, action) => { switch (action.type) { case "SET_ERROR": return { ...state, err: action.err }; case "SET_NUMBER": return { ...state, num: action.num }; case "SET_FIBO": return { ...state, computedFibs: [ ...state.computedFibs, { id: action.id, nth: action.nth, loading: action.loading }, ], }; case "UPDATE_FIBO": { const curr = state.computedFibs.filter((c) => c.id === action.id)[0]; const idx = state.computedFibs.indexOf(curr); curr.loading = false; curr.time = action.time; curr.fibNum = action.fibNum; state.computedFibs[idx] = curr; return { ...state }; } default: return state; } };
دعنا ننتقل إلى كل نوع إجراء واحدًا تلو الآخر.
-
SET_ERROR
: يعيّن حالة خطأ عند التشغيل. -
SET_NUMBER
: يعيّن القيمة في مربع الإدخال على الحالة. -
SET_FIBO
: يضيف مُدخلاً جديدًا إلى مصفوفة FNs المحسوبة. -
UPDATE_FIBO
: هنا نبحث عن إدخال معين واستبداله بكائن جديد يحتوي على FN المحسوب والوقت المستغرق لحسابه.
سنستخدم هذا المخفض قريباً. قبل ذلك ، دعنا ننشئ المكون الذي سيعرض FNs المحسوبة. قم بإنشاء ملف جديد src/Results.js
والصقه في الكود أدناه.
// src/Results.js import React from "react"; export const Results = (props) => { const { results } = props; return ( <div className="results-container"> {results.map((fb) => { const { id, nth, time, fibNum, loading } = fb; return ( <div key={id} className="result-div"> {loading ? ( <p> Calculating the{" "} <span className="bold"> {nth} </span>{" "} Fibonacci number... </p> ) : ( <> <p> Time: <span className="bold">{time} ms</span> </p> <p> <span className="bold"> {nth} </span>{" "} fibonnaci number:{" "} <span className="bold"> {fibNum} </span> </p> </> )} </div> ); })} </div> ); };
بهذا التغيير ، نبدأ عملية تحويل ملف index.html السابق إلى jsx. هذا الملف له مسؤولية واحدة: خذ مجموعة من الكائنات التي تمثل FNs المحسوبة واعرضها. الاختلاف الوحيد عما كان لدينا من قبل هو إدخال حالة التحميل . الآن عندما يتم تشغيل الحساب ، نعرض حالة التحميل لإعلام المستخدم بحدوث شيء ما.
دعنا نضع الأجزاء النهائية عن طريق تحديث الكود داخل src/App.js
الرمز طويل نوعًا ما ، لذلك سنفعل ذلك في خطوتين. دعنا نضيف أول كتلة من الكود.
import React from "react"; import "./App.css"; import { ordinal_suffix } from "./helpers"; import { reducer } from './reducer' import { Results } from "./Results"; function App() { const [info, dispatch] = React.useReducer(reducer, { err: "", num: "", computedFibs: [], }); const runWorker = (num, id) => { dispatch({ type: "SET_ERROR", err: "" }); const worker = new window.Worker('./fib-worker.js') worker.postMessage({ num }); worker.onerror = (err) => err; worker.onmessage = (e) => { const { time, fibNum } = e.data; dispatch({ type: "UPDATE_FIBO", id, time, fibNum, }); worker.terminate(); }; }; return ( <div> <div className="heading-container"> <h1>Computing the nth Fibonnaci number</h1> </div> <div className="body-container"> <p className="error"> {info.err} </p> // ... next block of code goes here ... // <Results results={info.computedFibs} /> </div> </div> ); } export default App;
كالعادة ، نأتي بوارداتنا. ثم نقوم بإنشاء مثيل لدالة الحالة والمُحدِّث باستخدام الخطاف useReducer. ثم نحدد وظيفة ، runWorker()
، تأخذ رقمًا ومعرفًا وتعيين حول استدعاء عامل الويب لحساب FN لهذا الرقم.
لاحظ أنه لإنشاء العامل ، نقوم بتمرير مسار نسبي إلى المنشئ العامل. في وقت التشغيل ، يتم إرفاق كود React الخاص بنا بالملف public/index.html
، وبالتالي يمكنه العثور على ملف fib-worker.js
في نفس الدليل. عند اكتمال الحساب (تم تشغيله بواسطة worker.onmessage
) ، يتم إرسال إجراء UPDATE_FIBO
، ويتم إنهاء العامل بعد ذلك. ما لدينا الآن لا يختلف كثيرًا عما كان لدينا من قبل.
في كتلة الإرجاع لهذا المكون ، نعرض نفس HTML الذي كان لدينا من قبل. نقوم أيضًا بتمرير مصفوفة الأرقام المحسوبة إلى مكون <Results />
للعرض.
دعنا نضيف الكتلة النهائية من الكود داخل تعليمة return
.
<div className="input-div"> <input type="number" value={info.num} className="number-input" placeholder="Enter a number" onChange={(e) => dispatch({ type: "SET_NUMBER", num: window.Number(e.target.value), }) } /> <button className="btn-submit" onClick={() => { if (info.num < 2) { dispatch({ type: "SET_ERROR", err: "Please enter a number greater than 2", }); return; } const id = info.computedFibs.length; dispatch({ type: "SET_FIBO", id, loading: true, nth: ordinal_suffix(info.num), }); runWorker(info.num, id); }} > Calculate </button> </div>
قمنا بتعيين معالج onChange
على الإدخال لتحديث متغير حالة info.num
. على الزر ، نحدد معالج حدث onClick
. عندما يتم النقر فوق الزر ، نتحقق مما إذا كان الرقم أكبر من 2. لاحظ أنه قبل استدعاء runWorker()
، نرسل أولاً إجراءً لإضافة إدخال إلى مجموعة FNs المحسوبة. سيتم تحديث هذا الإدخال بمجرد انتهاء العامل من وظيفته. بهذه الطريقة ، يحتفظ كل إدخال بموقعه في القائمة ، على عكس ما كان لدينا من قبل.
أخيرًا ، انسخ محتوى styles.css
من قبل واستبدل محتوى App.css
.
لدينا الآن كل شيء في مكانه. الآن ابدأ تشغيل خادم التفاعل الخاص بك واللعب ببعض الأرقام. لاحظ حالة التحميل ، والتي تعد بمثابة تحسين لتجربة المستخدم. لاحظ أيضًا أن واجهة المستخدم تظل مستجيبة حتى عند إدخال رقم يصل إلى 1000 والنقر فوق "حساب".

لاحظ حالة التحميل والعامل النشط. بمجرد حساب القيمة 46 ، يُقتل العامل ويتم استبدال حالة التحميل بالنتيجة النهائية.
- الكود المصدري لتطبيق React هذا متاح على Github وهناك تطبيق مستضاف على vercel.
خاتمة
تفو! لقد كانت مشوار طويل ، لذا فلنختتم الأمر. أنا أشجعك على إلقاء نظرة على إدخال MDN للعاملين على الويب (انظر قائمة الموارد أدناه) لمعرفة طرق أخرى لاستخدام العاملين على الويب.
في هذه المقالة ، تعرفنا على ماهية العاملين على الويب ونوع المشكلات التي من المفترض حلها. رأينا أيضًا كيفية تنفيذها باستخدام JavaScript عادي. أخيرًا ، رأينا كيفية تنفيذ عمال الويب في تطبيق React.
أنا أشجعك على الاستفادة من واجهة برمجة التطبيقات الرائعة هذه لتقديم تجربة أفضل لمستخدميك.
مزيد من الموارد
-
Console.time()
، مستندات الويب MDN - {JSON} ، الموقع الرسمي
- باستخدام Web Workers ، MDN web docs
- رقم فيبوناتشي ، ويكيبيديا
- عامل شرطي (ثلاثي) ، مستندات الويب MDN
-
Document
، واجهات برمجة تطبيقات الويب ، ومستندات الويب MDN - البدء ، إنشاء تطبيق React (مستندات)
-
Function.prototype.toString()
، مستندات الويب MDN - IIFE ، مستندات الويب MDN
-
workerSetup.js
، Awesome Fullstack Tutorials، GitHub - "البرمجة الموازية في JavaScript باستخدام Web Workers" ، عدي هيوارال ، متوسط