פונקציות גנרטור ב-TypeScript

פונקציות גנרטור ב-TypeScript

מאמר זה מסביר את פונקציות הגנרטור ב-TypeScript.

ניתן ללמוד הכל מהבסיס לגבי אופן השימוש בפונקציות גנרטור ועד דוגמאות מתקדמות שמשולבות עם עיבוד אסינכרוני, יחד עם דוגמאות קוד.

YouTube Video

פונקציות גנרטור

פונקציות גנרטור ב-TypeScript מספקות פונקציונליות דומה לפונקציות הגנרטור של JavaScript. פונקציות גנרטור מוגדרות באמצעות function* (הצהרת פונקציה עם כוכבית) והן פונקציות מיוחדות שיכולות להשהות ולהמשיך בביצוע שלא כמו פונקציות רגילות.

כאשר קוראים לפונקציית גנרטור, מוחזר איטרטור שמייצר ערכים אחד אחד באמצעות איטרטור זה, וניתן להשהות את הביצוע באמצעות מילת המפתח yield או לשלוח ערכים מבחוץ.

תחביר בסיסי של פונקציות גנרטור

 1function* myGenerator(): Generator<number, void, unknown> {
 2    yield 1;
 3    yield 2;
 4    yield 3;
 5}
 6
 7const gen = myGenerator();
 8
 9console.log(gen.next().value); // 1
10console.log(gen.next().value); // 2
11console.log(gen.next().value); // 3
12console.log(gen.next().done);  // true (Iteration finished)
  • הגדר פונקציית גנרטור באמצעות function* myGenerator().
  • מילת המפתח yield משהה את ביצוע הפונקציה ומחזירה ערך.
  • בכל פעם שמזמינים את המתודה next(), הביצוע של פונקציית הגנרטור ממשיך ומתקדם עד ל-yield הבא.

next() מחזיר אובייקט המכיל את הערך הבא ואת המאפיין done. כאשר done הוא true, הדבר מסמן שכל הערכים נוצרו ועיבוד הגנרטור הושלם.

שימושים בפונקציות גנרטור

שימוש בפונקציות גנרטור מאפשר ייצוג קל של עיבוד סדרתי. בדוגמה הבאה, אנו יוצרים פונקציית גנרטור שמייצרת רצף מספרים.

 1function* sequenceGenerator(start: number = 0, step: number = 1) {
 2    let current = start;
 3    while (true) {
 4        yield current;
 5        current += step;
 6    }
 7}
 8
 9const seq = sequenceGenerator(1, 2);
10
11console.log(seq.next().value); // 1
12console.log(seq.next().value); // 3
13console.log(seq.next().value); // 5
  • בדוגמה זו, sequenceGenerator מייצר רצף מספרים שממשיך עד אינסוף. השתמש ב-yield כדי להחזיר ערכים בכל שלב, ולייצר את הערך הבא בקריאות הבאות.

העברת ערכים ל-next

המתודה next() יכולה לקבל ערך שניתן לשלוח לפונקציית הגנרטור.

 1function* adder() {
 2    const num1 = yield;
 3    const num2 = yield;
 4    yield num1 + num2;
 5}
 6
 7const addGen = adder();
 8addGen.next();          // Initialization
 9addGen.next(5);         // Set 5 to num1
10const result = addGen.next(10).value; // Set 10 to num2 and get result
11console.log(result);    // 15
  • בדוגמה זו, next(5) ו-next(10) שולחים את הערכים שלהם לפונקציית הגנרטור, ו-yield num1 + num2 מחזיר את סכומם.

return ו-throw

  • return(value) יכול לסיים את הגנרטור ולהחזיר את הערך שצוין.
  • throw(error) יכול לזרוק חריג בתוך הגנרטור, ומשמש לטיפול בחריגים בתוך הגנרטור.
 1function* testGenerator() {
 2    try {
 3        yield 1;
 4        yield 2;
 5    } catch (e) {
 6        console.error("Error caught:", e);
 7    }
 8}
 9
10const gen = testGenerator();
11console.log(gen.next().value); // 1
12gen.throw(new Error("An error occurred!")); // Error caught: An error occurred!
  • בדוגמה זו, המתודה throw משמשת ליצירת שגיאה בתוך הגנרטור, ושגיאה זו נלכדת בתוך הגנרטור.

הגדרת סוג ב-TypeScript

ניתן להגדיר את סוג הפונקציה של מחולל (generator) בפורמט הבא.

1// Generator<YieldType, ReturnType, NextType>
2function* myGenerator(): Generator<number, void, unknown> {
3    yield 1;
4    yield 2;
5    yield 3;
6}
  • יש להגדיר את הסוגים בצורה של Generator<YieldType, ReturnType, NextType>.
    • YieldType הוא סוג הערך שמוחזר על ידי yield.
    • ReturnType הוא סוג הערך שמוחזר על ידי return.
    • NextType הוא סוג הערך שמועבר ל־next().

בדוגמה הבאה, סוגים ספציפיים מוגדרים לשימוש בטוח בגנרטור עם סוגים.

 1function* numberGenerator(): Generator<number, void, number> {
 2    const num1 = yield 1;
 3    const num2 = yield num1 + 2;
 4    yield num2 + 3;
 5}
 6
 7const gen = numberGenerator();
 8
 9console.log(gen.next().value);   // 1
10console.log(gen.next(10).value); // 12 (10 + 2)
11console.log(gen.next(20).value); // 23 (20 + 3)

גנרטורים ועיבוד אסינכרוני

ניתן להשתמש בגנרטורים גם לעיבוד אסינכרוני. לדוגמה, ניתן להשתמש ב-yield להמתנה לתוצאות של פעולות אסינכרוניות תוך המשך עיבוד לפי סדר. עם זאת, ב-TypeScript או JavaScript, משתמשים בדרך כלל ב-async/await.

1function* asyncTask() {
2    const result1 = yield fetch("https://codesparklab.com/json/example1.json");
3    console.log(result1);
4
5    const result2 = yield fetch("https://codesparklab.com/json/example2.json");
6    console.log(result2);
7}

לכן, למרות שניתן לעבד פעולות אסינכרוניות לפי סדר עם גנרטורים, הם אינם בשימוש נפוץ עבור עיבוד אסינכרוני מאחר ש-Promises ו-async/await נוחים יותר.

סיכום

  • פונקציות גנרטור הן פונקציות מיוחדות המוגדרות עם function* שיכולות להחזיר ערכים עם yield תוך עצירת ביצוע הפונקציה.
  • יש להשתמש ב-next() כדי להמשיך את הגנרטור ולקבל ערכים. בנוסף, ניתן לשלוח ערכים לגנרטור על ידי שימוש ב-next(value).
  • ניתן להשתמש ב-return() וב-throw() כדי לסיים פונקציות גנרטור או לטפל בשגיאות.
  • כאשר משתמשים בגנרטורים ב-TypeScript, ניתן להשתמש בהגדרות סוג כדי לכתוב קוד בטוח סוג.

גנרטורים הם כלים חזקים המאפשרים לך לשלוט באופן גמיש בתהליך האיטרציה.

תוכלו לעקוב אחר המאמר שלמעלה באמצעות Visual Studio Code בערוץ היוטיוב שלנו. נא לבדוק גם את ערוץ היוטיוב.

YouTube Video