Menu

Sleep w TypeScript: jak czekać lub opóźnić wykonanie

TypeScript nie ma wbudowanego sleep, ale jedna linia wystarczy, żeby go mieć: funkcja zwracająca Promise<void>, który rozwiązuje setTimeout. Zobacz, jak na niego czekać przez await, jak robić pauzy w pętlach, ponawiać z opóźnieniem, anulować sleep i dlaczego w JavaScripcie nie ma blokującego sleep.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

TypeScript nie ma wbudowanego sleep. Napisz funkcję zwracającą Promise<void>, który setTimeout rozwiązuje po upływie opóźnienia, a potem użyj na nim await wewnątrz funkcji async:

await sleep(300) wstrzymuje tylko main. Program nie jest zamrożony: timery, inne funkcje async i operacje wejścia/wyjścia działają dalej, gdy on czeka.

Jak działa funkcja sleep

setTimeout(resolve, ms) planuje wywołanie resolve po ms milisekundach, a wywołanie resolve rozstrzyga promise. await na tym promise'ie zawiesza funkcję async do tego momentu.

Argument typu <void> ma znaczenie. Samodzielne new Promise(...) nie wnioskuje swojego typu z wywołań resolve, więc bez niego sleep zwracałoby Promise<unknown>. A void pozwala wywołać resolve bez wartości: w Promise<number> resolve() bez argumentu to błąd kompilacji. Gdy funkcja ma zadeklarowany typ zwracany, argument typu może trafić tam:

setTimeout gwarantuje minimalny, a nie dokładny czas: callback uruchamia się, gdy opóźnienie minie i pętla zdarzeń jest wolna. Licz się z kilkoma milisekundami zapasu.

Sleep wewnątrz pętli

await sleep() wewnątrz pętli for lub for...of robi pauzę między iteracjami i tak właśnie ustala się tempo pracy (limity zapytań, odpytywanie, animacje w terminalu):

Nie działa to wewnątrz forEach: ignoruje on promise zwracany przez każdy callback async, więc wszystkie iteracje startują naraz i nic nie czeka. Użyj pętli for lub for...of, jak wyjaśnia strona async/await.

Ponawianie z opóźnieniem

Częste praktyczne zastosowanie: spróbuj wykonać operację, a jeśli się nie uda, poczekaj i spróbuj ponownie, za każdym razem podwajając czas oczekiwania (exponential backoff).

return await fn() wewnątrz try ma znaczenie: bez await odrzucony promise zostałby zwrócony, zanim catch zdążyłby go zobaczyć.

Sleep, który można anulować

Zwykłego sleep nie da się przerwać. Aby przestać czekać wcześniej (użytkownik nacisnął Anuluj, zaczęło się zamykanie), przekaż AbortSignal i wyczyść timer, gdy sygnał zadziała:

Program kończy się zaraz po przerwaniu, bo długi timer został wyczyszczony. Bez clearTimeout Node działałby dalej, aż minęłoby 5 sekund.

Wbudowane timery oparte na promise'ach w Node

Node dostarcza wersję setTimeout opartą na promise'ach w module node:timers/promises, więc w kodzie dla Node możesz ją zaimportować zamiast pisać funkcję pomocniczą. Przyjmuje też wartość, którą ma się rozwiązać, i 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
}

Typy pochodzą z @types/node (npm i -D @types/node). Ten moduł nie istnieje w przeglądarkach, gdzie przenośnym wyborem jest jednolinijkowa funkcja pomocnicza.

Nie ma blokującego sleep

Języki z wątkami mają sleep, który blokuje bieżący wątek. JavaScript wykonuje twój kod na jednym wątku z pętlą zdarzeń, więc blokujący sleep zamroziłby wszystko inne: żadne timery by nie odpaliły, żadne odpowiedzi sieciowe nie byłyby obsłużone, a w przeglądarce strona przestałaby reagować. Pętla, która kręci się, dopóki zegar nie minie określonego czasu, robi dokładnie to i nigdy nie jest właściwym narzędziem.

Asynchroniczny sleep to sposób, w jaki działa czekanie w JavaScripcie i TypeScripcie: funkcja jest zawieszona, wątek jest wolny, a funkcja wznawia działanie, gdy odpali timer. Atomics.wait naprawdę potrafi blokować, ale tylko na SharedArrayBuffer, a przeglądarki zabraniają go w głównym wątku; istnieje do koordynacji wątków workerów, a nie do opóźnień.

Najczęściej zadawane pytania

Jak zrobić sleep w TypeScript?

Zdefiniuj const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)); i wywołaj await sleep(1000) wewnątrz funkcji async. To wstrzymuje tę funkcję na około sekundę, nie blokując reszty programu.

Czy TypeScript ma wbudowaną funkcję sleep?

Nie. TypeScript dodaje typy do JavaScriptu i nie ma własnej biblioteki uruchomieniowej, a JavaScript nie ma sleep. W Node możesz ją zaimportować: import { setTimeout as sleep } from "node:timers/promises". W przeglądarce napisz jednolinijkową funkcję pomocniczą.

Dlaczego sleep wymaga await?

sleep(1000) od razu zwraca promise; dopiero await wstrzymuje otaczającą funkcję async, dopóki promise się nie rozwiąże. Bez await następna linia wykonuje się natychmiast. await działa tylko w funkcji async (lub na najwyższym poziomie modułu ES).

Czy mogę zablokować wątek synchronicznym sleep w TypeScript?

Nie w normalnym kodzie. JavaScript działa na jednym wątku z pętlą zdarzeń, a pętla aktywnego czekania zamraża wszystko: timery, callbacki sieciowe, całą stronę. Zamiast tego używaj asynchronicznego sleep. Atomics.wait może zablokować wątek w Node lub w workerze, ale służy do niskopoziomowej koordynacji workerów, a nie do opóźnień.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ