Menu

TypeScript Sleep: איך לחכות או להשהות ב-TypeScript

ל-TypeScript אין sleep מובנה, אבל שורה אחת נותנת לכם אחד: פונקציה שמחזירה Promise<void> ש-setTimeout מסיים. למדו איך עושים לה await, איך משהים בתוך לולאות, איך מנסים שוב עם השהיה, איך מבטלים sleep, ולמה אין ב-JavaScript sleep חוסם.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

ל-TypeScript אין sleep מובנה. כתבו פונקציה שמחזירה Promise<void> ש-setTimeout מסיים אחרי ההשהיה, ואז עשו לה await בתוך פונקציית async:

await sleep(300) משהה רק את main. התוכנית לא קופאת: טיימרים, פונקציות async אחרות ו-I/O ממשיכים לרוץ בזמן ההמתנה.

איך פונקציית sleep עובדת

setTimeout(resolve, ms) מתזמן קריאה ל-resolve אחרי ms מילישניות, והקריאה ל-resolve מסיימת את ה-promise. await על ה-promise הזה משעה את פונקציית ה-async עד אז.

לארגומנט הטיפוס <void> יש חשיבות. new Promise(...) לבדו לא מסיק את הטיפוס שלו מהקריאות ל-resolve, ולכן בלעדיו sleep הייתה מחזירה Promise<unknown>. ו-void הוא מה שמאפשר לקרוא ל-resolve בלי ערך: ב-Promise<number>, resolve() בלי ארגומנט היא שגיאת קומפילציה. כשלפונקציה יש טיפוס החזרה מוצהר, ארגומנט הטיפוס יכול לעבור לשם במקום:

setTimeout מבטיח זמן מינימלי, ולא זמן מדויק: ה-callback רץ כשההשהיה עברה וה-event loop פנוי. צפו לכמה מילישניות של סטייה.

Sleep בתוך לולאה

await sleep() בתוך לולאת for או for...of משהה בין איטרציות, וכך מווסתים עבודה (מגבלות קצב, polling, אנימציות בטרמינל):

זה לא עובד בתוך forEach: הוא מתעלם מה-promise שכל callback אסינכרוני מחזיר, ולכן כל האיטרציות מתחילות בבת אחת ושום דבר לא מחכה. השתמשו בלולאת for או for...of, כמו שמוסבר בדף async/await.

ניסיון חוזר עם השהיה

שימוש אמיתי נפוץ: לנסות פעולה, ואם היא נכשלת, לחכות ולנסות שוב, ולהכפיל את זמן ההמתנה בכל פעם (exponential backoff).

ה-return await fn() בתוך try חשוב: בלי await, promise שנדחה היה מוחזר לפני שה-catch היה יכול לראות אותו.

Sleep שאפשר לבטל

sleep רגיל אי אפשר לקטוע. כדי להפסיק לחכות מוקדם (משתמש לחץ על ביטול, התחיל כיבוי), העבירו AbortSignal ונקו את הטיימר כשהוא מופעל:

התוכנית יוצאת מיד אחרי הביטול כי הטיימר הארוך נוקה. בלי clearTimeout, Node היה ממשיך לרוץ עד שחמש השניות היו עוברות.

טיימרי ה-promise המובנים של Node

Node כולל גרסת promise של setTimeout במודול node:timers/promises, כך שבקוד Node אפשר לייבא אותה במקום לכתוב את פונקציית העזר. היא מקבלת גם ערך לסיים איתו ו-AbortSignal:

import { setTimeout as sleep } from "node:timers/promises";

async function main() {
    await sleep(1000);                              // wait one second
    const result = await sleep(500, "done");        // resolves with "done"
    const ac = new AbortController();
    await sleep(10_000, undefined, { signal: ac.signal }); // cancellable
}

הטיפוסים מגיעים מ-@types/node (npm i -D @types/node). המודול הזה לא קיים בדפדפנים, ושם פונקציית העזר של שורה אחת היא הבחירה הניידת.

אין sleep חוסם

בשפות עם threads יש sleep שחוסם את ה-thread הנוכחי. JavaScript מריצה את הקוד שלכם על thread יחיד עם event loop, ולכן sleep חוסם היה מקפיא את כל השאר: שום טיימר לא מופעל, שום תגובת רשת לא מטופלת, ובדפדפן הדף מפסיק להגיב. לולאה עסוקה שמסתובבת עד שהשעון עובר זמן מסוים עושה בדיוק את זה, והיא אף פעם לא הכלי הנכון.

ה-sleep האסינכרוני הוא הדרך שבה המתנה עובדת ב-JavaScript וב-TypeScript: הפונקציה מושעית, ה-thread פנוי, והפונקציה ממשיכה כשהטיימר מופעל. Atomics.wait באמת יכול לחסום, אבל רק על SharedArrayBuffer, ודפדפנים אוסרים אותו ב-main thread; הוא קיים לתיאום worker threads, ולא להשהיות.

שאלות נפוצות

איך עושים sleep ב-TypeScript?

הגדירו const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)); וקראו ל-await sleep(1000) בתוך פונקציית async. זה משהה את הפונקציה הזו לשנייה בערך, בלי לחסום את שאר התוכנית.

האם יש פונקציית sleep מובנית ב-TypeScript?

לא. TypeScript מוסיף טיפוסים ל-JavaScript ואין לו ספריית זמן ריצה משלו, ול-JavaScript אין sleep. ב-Node אפשר לייבא אחת: import { setTimeout as sleep } from "node:timers/promises". בדפדפן, כתבו את פונקציית העזר של שורה אחת.

למה sleep צריך await?

sleep(1000) מחזיר promise מיד; רק await משהה את פונקציית ה-async שמסביב עד שה-promise מסתיים. בלי await, השורה הבאה רצה מיד. await עובד רק בפונקציית async (או ברמה העליונה של מודול ES).

האם אפשר לחסום את ה-thread עם sleep סינכרוני ב-TypeScript?

לא בקוד רגיל. JavaScript רצה על thread אחד עם event loop, ולולאת busy-wait מקפיאה הכול: טיימרים, callbacks של רשת, את הדף כולו. השתמשו במקום זאת ב-sleep האסינכרוני. Atomics.wait יכול לחסום thread ב-Node או ב-worker, אבל הוא מיועד לתיאום workers ברמה נמוכה, ולא להשהיות.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל