מחילת הארנב - היכרות עם JavaScript Iterator
כמפתחי Front End אנחנו נדרשים לעמוד בקצב המשוגע של השנים האחרונות. JavaScript מתפוצצת מחידושים מכל הכיוונים: הסטנדרט עצמו קיבל בוסט ב 3 שנים האחרונות מאז שוחררה ECMAScript 2015 או בשמה הסקסי es6 (והמהדרין ירשמו 6ES), כששנים לפני כן נוצרו מלאנתלפים ספריות וסביבות עבודה כמו פיטריות אחרי הגשם, כשהפיטריה הגדולה שבהן היא כמובן Nodejs הגאונית, עליה יושב הזחל-מלך, מעלה עשן מהנרגילה בטעם npm. והעשן הזה ממלא כמעט את כל החלל בממלכת JavaScript, בואכה es9.
זה היה Release עצבני אתמול, כולם היו על הרגליים, או יותר נכון על הכסאות, והבוקר החלטתי להגיע ממש מוקדם, בעשר, כדי לוודא תקינות.
הדלקתי את המכונה והרגשתי שעשיתי מספיק בינתיים וצריך הפסקה, אז לפני שמחשבה פרקטית אחרת תתגנב לראשי, מיד קמתי והלכתי לקפיטריה. קפה, איזה צנים עם ממרח טעם כלשהו ולקינוח עוגית.
חזרתי לעמדה צ'יק צ'אק תוך חצי שעה, ומיד שמתי לב למייל האדום. כן, זה המייל האוטומטי הזה שמודיע לי שיש בעיה שקשורה לקוד שלי. נאמר בו בשפה בוטה שיש בעיית זיכרון במודול שלי. "מה, הוא לא זוכר אותי??" חשבתי, "רק אתמול יצרתי אותו, הסנוב, וכבר נשכחתי…".
לגמתי מהקפה והתחלתי לרפרף בקוד, פירורי צנים טעים על המקלדת (עוגית ממתינה בזווית העין). "מה העניין?" הרהרתי, "מודול שבו יש אוסף של לקוחות ובין היתר מאפשר לחפש לקוח או מחזיר אותם למשתמש כדי שהוא יעשה מה שבא לו כמו מעבר עליהם, מחיקת ועדכון לקוח וכו'… הכל נראה תקין". פה חשדתי! ברגע ש"הכל נראה תקין" אני יודע שכנראה לא הכל תקין, שהרי קיבלתי בוקר אדום טוב.
זה דורש הירהור נוסף, ואין כמו להרהר בשירותים.
כשפתחתי את דלת השירותים קרה לי משהו יותר מוזר מכך שהמודול שלי שכח אותי: נשאבתי דרך מחילת ארנב.
הגעתי לעולם מלא קסם. אני אוהב הפסקות קסומות כאלה למרות שהעוגית שלי ממתינה לי ליד המקלדת.
הכל ירוק ומגניב מסביב, רוח אוקטאלית מזרחית גילגלה עלים על האדמה בקבוצות של 8, ואז שמתי לב לשביל בין השיחים שתלוי על ענפיו שלט:
איטרטור? מה לעזאזל… ואז שמתי לב שמרחוק מגיעות צהלות וצעקות אימה, מלוות במוזיקת קרקס שמחה, ומיד ידעתי שהשביל לא מוביל למשרד של עופרה מה HR, אז התחלתי ללכת בשביל מוקף השיחים/עצים.
כשנגמר השביל עמדתי מול רכבת הרים מפלצתית בגודל היסטרי, כמו הקוד-בייס שלנו. לא ראיתי את סופה. בשער עמד איש מבוגר, שיער לבן וזקן עוד יותר לבן, מגבעת לראשו ומבט מטורלל קצת.
"שלום מר איש, הו הו הו" הוא כנראה הכרטיסן "הגעת ל Iterator Roller-Coaster, היכן הכרטיס'שך? הו הו הו"
"אאאא.. אין לי… מקבלים תן-ביס במקרה?".
האיש רכן לעברי וחייך. אני יכול להישבע שהייתה לו שן מתנדנדת. "אין כרטיס אז לך לרכבת ההרים הקטנה" והצביע לי לכיוון מבנה לא רחוק.
שם המתין לי כרטיסן נוסף. "שלום מר איש, הי הי הי" זה לא שיהוקים חשבתי, כל הכרטיסנים כנראה פגועי רכבות הרים.
"אפשר לעלות על הרכבת?" שאלתי.
"כמובן, רק תמתין כאן שכל הקרונות יגיעו"
הוצאתי את השעון מהכיס ואחרי 5 דקות שאלתי אותו מה נסגר.
"סבלנות, יש לנו 1,000 קרונות. אתה יודע שבשנת 70 ניהלתי רכבת הרים בעלת 50,000 קרונות??"
בהיתי בו. ואז עוד קצת. "מה… מה אלף קרונות?" חושב לעצמי שהלכה ההפסקה שלי.
"תצטרך להמתין עד שיגיעו כל הקרונות"
"אבל למה לא פיצלתם אותם? למשל זוגות זוגות או עשיריות? משהו?"
"אה, זה, זה לא כאן. זאת רכבת ישנה שבה כל הקרונות, ולא משנה כמה יש, מחוברים אחד לשני כמיקשה אחת" הוא חייך עם השן הזאת "תצטרך להמתין".
"יותר מזה" פתאום הוא הרים את הקול: "אתה יודע כמה תלונות מגיעות מאיש התחזוקה שלנו? שבוע שעבר היה לחץ נוראי כי היה חג ה Happy Hour. קרון מס' 70 התקלקל, אז הוא המתין בדיוק כמוך לאלף הקרונות רק כדי לתקן את אותו קרון…"
בהיתי בו. ואז עוד קצת. השן המתנדנדת הזו מרתקת… איך הוא מצליח לדבר איתה ככה… שלא תתנק ותעוף עלי בזמן שאני צורח…
הבנתי שכאן לא אקבל את מנת האדרנלין שלי וחזרתי לרכבת הרים איטרטור.
לפתע הבנתי שכאן הכל זריז יותר והכרטיסן קורא לקרון לפי דרישה: כשמגיע לקוח עם כרטיס.
שאלתי אותו "תגיד שמעת על התקלה שקרתה בשבוע שעבר ברכבת הישנה?"
"הו הו הו, כמובן, זאת הייתה שיחת היום בארמון המלכה. ואכן, ECMA התחילה להסיט לקוחות לכאן במקום לרכבת הישנה" אמר בסיפוק.
"ולמה קוראים לה Iterator?"
"אם אגיד לך אז אצטרך להרוג אותך"
צחקנו המון.
"צ'מע מר איש, אנחנו מעבירים סדנא זריזה למבקרים חדשים, כנס בדלת הלא חשודה הזאת."
אין ידית, אבל כמו בכל הזיה נפתחה הדלת מאליה והופה! אני בתוך כיתת מחשבים. התיישבתי ליד השולחן הראשון והסתכלתי מסביב, מחפש את פינת הקפה.
משומקום התיישב לידי מטורלל צעיר. מיד ידעתי שזה הבן של הכרטיסן כי גם לו הייתה שן מתנדנדת, אבל בצד השני של הפה. בטח קיבל את הירושה מאמא. "ממצב גיבור? שמי קארד ואין לנו הרבה זמן כי צפויים לנו הרבה מבקרים לקראת ה Happy Hour, אז יאללה לעניינים: הבנתי מאבא שביקרת בשתי רכבות ההרים, ראית את ההבדל".
"בהחלט, תשמע…" רציתי להגיד לו שלא ידבר ישירות אלי (השן הזאת משגעת אותי), אבל קארד המשיך:
"אז ECMA יצרה קסם חדש שהינחה אותנו ברכבת החדשה. זה נקרא Iterator. הרעיון הוא שיש לך אוסף של מידע, ואתה צריך לאפשר לקבל אותם לפי דרישה. ברכבת הישנה כל הקרונות היו כאחד ולכן היית צריך להמתין, ברכבת החדשה, כשלקוח מגיע עם כרטיס, מייד מגיע קרון. בקיצור: כשיש דרישה לקרון – מגיע הקרון הבא. לא מגיע קרון נוסף כל עוד אין דרישה".
"המממ… אז מה היה קורה אם היה מתקלקל קרון מס' 70 ברכבת החדשה?" היקשתי.
"אז אבא שלי היה קורא קרון קרון, אחד אחד, עד שקרון מס' 70 היה מגיע. זהו. אין צורך בכל שאר הקרונות" ואז הוא לחש לי "אתה יודע שבחדשה יש מעל 100,000 קרונות?"
ואני רק חושב על המייל האדום שלי ומבין מה הבעיה שלי: אני כנראה מחזיר אוספים גדולים של הלקוחות שלנו במקום לאפשר את מה שהוא מדבר איתי.
"אז מה עושים כשיש בעיה כזו אצלי בקוד?" שאלתי מודאג.
"נגיד והיית רוצה להדפיס את כל האותיות במחרוזת. אחת אחת. איך היית עושה זאת?"
הוא התחיל לרשום. "זו דרך נאיבית, אולי הבסיסית ביותר:"
"נכון" עניתי "ויש גם את for of. שיניתי את הקוד שלו והלכתי להכין קפסיטו:
"אוה! על זה רציתי לדבר איתך" קפץ הבן של.
"שים לב שבקוד שלי ביצעתי לולאת for פשוטה וקראתי לפונקציה charAt שהחזירה לי את התו המבוקש, ובקוד שלך אתה מבצע איטרציה על המחרוזת כאובייקט! מה זה אומר לך?"
מעמדת הקפה עניתי לו: "שהאובייקט הסטרינגי נותן לי תו בכל איטרציה! אבל איך זה קורה?"
"for of בעצם זה מנגנון איטרטיבי שמבקש מהאובייקט את הערך הבא. האובייקט צריך להיות כזה שניתן לבצע עליו איטרציות."
וואו. חיזקתי את הקפה בעוד חצי כפית. הוא המשיך בהתלהבות:
"זאת אומרת שיש כאן הבנה של שני הצדדים אחד את השני: for of מצפה מהאובייקט לשני דברים: הראשון לדעת בכל פעם האם יש עוד ערך, ואם יש, וזה הדבר השני, לקבל אותו. האובייקט מצידו צריך לספק את שני הדברים הללו. זה מה נקרא פרוטוקול: סט חוקים שכל הצדדים פועלים לפיו".
"“The Iterator Protocol (עם תופים כאלה ברקע).
"כל מה שהאובייקט צריך לעשות זה לממש את הפרוטוקול כדי להיחשב כ iterator. ואז, כשהוא מתבקש ע"י for of להזרים מידע, הוא מחזיר אובייקט שמממש את פרוטוקול ה Iterator. כך for of מתנהל מול ה iterator הזה וכולם שמחים"
"תגיד, יש עוד קפה?" חייכתי כמבקש דוגמא
הוא מלמל cls משהו והמסך התרוקן לפתע. קטע.
"לאובייקט שיצרתי יש מתודה שנקראת next. המתודה מחזירה אובייקט עם הערך הבא, value, ו done כדי לתת תשובה האם יש טעם לקרוא שוב ל next."
"אז למה פשוט לא להשתמש בו ב for of?" שאלתי, ככה:
מייד כמובן הרצתי ובאתי לעשות קומיט, בנוהל. קיבלתי:
TypeError: obj is not iterable
"אבל יש לו next שמחזיר אובייקט שמכיל done ו value כמו שהפרוטוקול דורש" הקול שלי היה גבוה מוזר קצת.
"אחלה! אז בוא נעשה קצת סדר:
אובייקט שהוא Iterableמחזיר אובייקט Iterator.
"זה בגלל שצריך מנגנון שיהיה זהה לכווווול מי שרוצה להיות Iterator, כולל כמוך שנידחפים. תחשוב של for of לא באמת איכפת מי אתה, הוא רק צריך ממך שתחזיר לו אובייקט שיש לו next כמו שראינו. אז for of צריך דרך אחידה, מכל Iterable וואט סו אבר, שדרכה יקבל Iterator"
"ומה היא הדרך ש ECMA המלכה ברוב חוכמתה קבעה? שימוש ב Symbol.iterator" וחיוך טיפשי על פניו.
בהיתי בו.
"נו, for of יחפש את התכונה [Symbol.iterator] שאמורה להיות פונקציה שמחזירה iterator! וקיבלת דרך אחידה":
"אז המנגנון הדיפולטי פונה ל Iterable ומחפש את [Symbol.iterator] שאמור להחזיר לו אובייקט שמממש את iterator protocol עם ה next וכו'. הנה עוד דוגמא הפעם עם spread:
מהדוגמא הזו הבנתי שגם spread מבצע שימוש פנימי ב Iterator.
ביקשתי ממנו שייתן לי הסבר זריז על Symbol.
"Symbol הוא מבנה בסיסי (כמו number) שהמלכה ECMA הוסיפה לממלכה. כל Symbol() הוא ייחודי! הוא יכול לשמש כמזהה לתכונות אובייקט. ECMA בעצם הוסיפה יכולות חדשות בלי לפגוע בקיים! מלכה כבר אמרתי?!"
"אז אתה מבין? כל Iterable מחליט באיזה אופן הוא מחזיר את הדאטה הפנימי שלו. הוא מחליט מתי לסגור את הבאסטה. נגיד שאנחנו רוצים להחזיר את התווים רק בזוגות ורק את 3 הזוגות הראשונים? הנה דוגמא פשוטה"
"גם Array הוא Iterable:"
נורת לד נדלקה מעל ראשי. "חשוך כאן קצת" אמרתי.
"סתם, הבנתי למה קיבלתי מייל אדום הבוקר. כשאני מחזיר את כל האוסף שלי אני מעתיק את הלקוחות למערך חדש שאותו אני מחזיר… What a waste, אז אין לי צורך בכך כי אני יכול להיות Iterable!"
"בום עליך!" התנדנדה השן. הוא נראה מרוצה וחידד:
"המשתמש לא אמור לדעת כמה יש באוסף הפנימי של ה Iterable, אלא לקבל תשובה האם יש עוד, ואם יש, לקבל את הדאטה. יכול להיות שהמשתמש יקבל הודעה שאין יותר, למרות שיש, אבל ה Iterable החליט שמספיק"
"צ'מע son, אני מבין כעת מה היתרונות של זה. אני, בתור Iterable יכול לשלוט באופן שבו המידע הפנימי שלי מונגש החוצה. Cool"
ידעתי שהיום, בעקבות הגילויים, אני חוזר הביתה מוקדם. גג 22:00.
הבן עם השן סגר את המחשב וניגש למגב והדלי שהיו סמוך. "תודה שהיגעת למרכז המבקרים של Iterator Roller-Coaster" אמר "לפני שמגיעים ים המבקרים אבקשך להתפנות במיידי".
אני באמת צריך לשירותים.
בדרכי חזרה בשביל המתפתל חשבתי על ה Iterator pattern ואיזה פיקס מהסרטים אני הולך לתת בקוד שלי. ואיזה גיבור אני שעמדתי בגבורה אל מול השיניים.
יצאתי ממחילת הארנב, והופ אני בחזרה בשירותים. הידחתי את המים, והתיישבתי מול המסך בקיוביק.
יאללה לעבודה! יש לי פיקס על הראש 😊
אבל לפני כן – הפסקת קפה. אני צריך לחשוב על זה…