حول المولدات
المولدات هي نوع جديد من الوظائف تم تقديمه في ES6. تم كتابة العديد من المقالات عنها والعديد من الأمثلة النظرية. بالنسبة لي ، كتاب أنت لا تعرف JS ، جزء من عدم التزامن والأداء ، ساعد في توضيح جوهر المولدات وكيفية استخدامها . من بين جميع كتب JS التي درستها ، هذا الكتاب هو الأكثر امتلاءًا بالمعلومات المفيدة بدون ماء.
تخيل أن المولد (الوظيفة في الإعلان ، وهي *) هو نوع من الأجهزة الكهربائية مع لوحة تحكم عن بعد. بعد إنشاء المولد وتركيبه (إعلان الوظيفة) ، تحتاج إلى "تدويره" (تنفيذ هذه الوظيفة) بحيث يدور بسرعة خاملة و "يغذي" لوحة التحكم بنفسه (عند تنفيذ وظيفة المولد ، فإنه يعيد مكررًا). تحتوي لوحة التحكم هذه على زرين: ابدأ (استدعاء الطريقة التالية للمكرر لأول مرة) و التالي (المكالمات اللاحقة إلى الطريقة التالية للمكرر). بعد ذلك ، باستخدام لوحة التحكم هذه ، يمكنك التسرع في جميع أنحاء محطة الطاقة بالكامل (وفقًا لتطبيقنا) وعندما تحتاج إلى طاقة كهربائية (بعض القيم من وظيفة المولد) ، اضغط على الزر التالي في جهاز التحكم عن بُعد (قم بتنفيذ الطريقة التالية () للمولد).ينتج المولد المقدار المطلوب من الكهرباء (يُرجع قيمة معينة من خلال العائد) وينتقل إلى وضع الخمول مرة أخرى (تنتظر وظيفة المولد المكالمة التالية من المكرر). تستمر الحلقة طالما أن المولد قادر على إنتاج الكهرباء (هناك بيانات ناتجة) أو أنها لن تتوقف (تتم مواجهة العودة في وظيفة المولد).
وفي هذا القياس برمته ، النقطة الأساسية هي لوحة التحكم (مكرر). يمكن تمريره إلى أجزاء مختلفة من التطبيق وفي الوقت المناسب "أخذ" القيم من المولد. لإكمال الصورة ، يمكنك إضافة عدد غير محدود من الأزرار على لوحة التحكم لبدء تشغيل المولد في أوضاع معينة (تمرير المعلمات إلى الطريقة التالية (أي معلمات) للمكرر) ، ولكن زرين كافيين لتنفيذ الخطاف.
الخيار 4. مولد بدون وعود
يتم توفير هذا الخيار من أجل الوضوح ، لأن تعمل المولدات بكامل قوتها مع وعود (آلية انتظار / غير متزامنة). لكن هذا الخيار يعمل وله الحق في الوجود في مواقف بسيطة معينة.
أقوم بإنشاء متغير في الخطاف لتخزين المرجع إلى المكرر (خلية لوحة التحكم في المولد)
const iteratorRef = useRef(null);
. . , next() ( next). :
const updateCounter = () => {
iteratorRef.current.next();
};
const checkImageLoading = (url) => {
const imageChecker = new Image();
imageChecker.addEventListener("load", updateCounter);
imageChecker.addEventListener("error", updateCounter);
imageChecker.src = url;
};
. , , , next . , " ". dispatch , . :
function* main() {
for (let i = 0; i < imgArray.length; i++) {
checkImageLoading(imgArray[i].src);
}
for (let i = 0; i < imgArray.length; i++) {
yield true;
dispatch({
type: ACTIONS.SET_COUNTER,
data: stateRef.current.counter + stateRef.current.counterStep
});
}
}
"" , ( iteratorRef. ( next ).
.
import { useReducer, useEffect, useLayoutEffect, useRef } from "react";
import { reducer, initialState, ACTIONS } from "./state";
const PRELOADER_SELECTOR = ".preloader__wrapper";
const PRELOADER_COUNTER_SELECTOR = ".preloader__counter";
const usePreloader = () => {
const [state, dispatch] = useReducer(reducer, initialState);
const stateRef = useRef(state);
const iteratorRef = useRef(null);
const preloaderEl = document.querySelector(PRELOADER_SELECTOR);
const counterEl = document.querySelector(PRELOADER_COUNTER_SELECTOR);
const updateCounter = () => {
iteratorRef.current.next();
};
const checkImageLoading = (url) => {
const imageChecker = new Image();
imageChecker.addEventListener("load", updateCounter);
imageChecker.addEventListener("error", updateCounter);
imageChecker.src = url;
};
useEffect(() => {
const imgArray = document.querySelectorAll("img");
if (imgArray.length > 0) {
dispatch({
type: ACTIONS.SET_COUNTER_STEP,
data: Math.floor(100 / imgArray.length) + 1
});
}
function* main() {
for (let i = 0; i < imgArray.length; i++) {
checkImageLoading(imgArray[i].src);
}
for (let i = 0; i < imgArray.length; i++) {
yield true;
dispatch({
type: ACTIONS.SET_COUNTER,
data: stateRef.current.counter + stateRef.current.counterStep
});
}
}
iteratorRef.current = main();
iteratorRef.current.next();
}, []);
useLayoutEffect(() => {
stateRef.current = state;
if (counterEl) {
stateRef.current.counter < 100
? (counterEl.innerHTML = `${stateRef.current.counter}%`)
: hidePreloader(preloaderEl);
}
}, [state]);
return;
};
const hidePreloader = (preloaderEl) => {
preloaderEl.remove();
};
export default usePreloader;
.
5.
. next ( ). ( ).
:
const getImageLoading = async function* (imagesArray) {
for (const img of imagesArray) {
yield new Promise((resolve, reject) => {
const imageChecker = new Image();
imageChecker.addEventListener("load", () => resolve(true));
imageChecker.addEventListener("error", () => resolve(true));
imageChecker.src = img.url;
});
}
};
:
for await (const response of getImageLoading(imgArray)) {
dispatch({
type: ACTIONS.SET_COUNTER,
data: stateRef.current.counter + stateRef.current.counterStep
});
}
for await ... of. Next.
- . , , .
import { useReducer, useEffect, useRef } from "react";
import { reducer, initialState, ACTIONS } from "./state";
const PRELOADER_SELECTOR = ".preloader__wrapper";
const PRELOADER_COUNTER_SELECTOR = ".preloader__counter";
const usePreloader = () => {
const [state, dispatch] = useReducer(reducer, initialState);
const stateRef = useRef(state);
const preloaderEl = document.querySelector(PRELOADER_SELECTOR);
const counterEl = document.querySelector(PRELOADER_COUNTER_SELECTOR);
useEffect(() => {
async function imageLoading() {
const imgArray = document.querySelectorAll("img");
if (imgArray.length > 0) {
dispatch({
type: ACTIONS.SET_COUNTER_STEP,
data: Math.floor(100 / imgArray.length) + 1
});
for await (const response of getImageLoading(imgArray)) {
dispatch({
type: ACTIONS.SET_COUNTER,
data: stateRef.current.counter + stateRef.current.counterStep
});
}
}
}
imageLoading();
}, []);
useEffect(() => {
stateRef.current = state;
if (counterEl) {
stateRef.current.counter < 100
? (counterEl.innerHTML = `${stateRef.current.counter}%`)
: hidePreloader(preloaderEl);
}
}, [state]);
return;
};
const getImageLoading = async function* (imagesArray) {
for (const img of imagesArray) {
yield new Promise((resolve, reject) => {
const imageChecker = new Image();
imageChecker.addEventListener("load", () => resolve(true));
imageChecker.addEventListener("error", () => resolve(true));
imageChecker.src = img.url;
});
}
};
const hidePreloader = (preloaderEl) => {
preloaderEl.remove();
};
export default usePreloader;
:
:
useRef ( )
كيفية التحكم في تدفق الأحداث باستخدام المولدات ، ولكن دون استخدام الوعود (باستخدام الاسترجاعات)
كيفية التحكم في تدفق الأحداث مع المتعاملين الموعدين باستخدام المولدات و a for ... of loop
رابط Sandbox
رابط المستودع
أن تستمر ... ملحمة إعادة ...