名前から既にワクワクするこのAPIは、なんとPromiseを返すsetTimeout、setInterval関数を提供しています!最高です…
というわけで今回はそれの紹介です。
基本的な使い方
await setTimeout(1000)
←これができるんです!素晴らしくないですか??
top-level await や for-awaitと組み合わせるとこんな感じで書けます
import { setTimeout } from 'timers/promises'; console.log('start'); await setTimeout(1000); // これでいける!! console.log('1s passed');
import { setInterval } from 'timers/promises'; console.log('start'); for await (const startAt of setInterval(1000, Date.now()) { console.log(Date.now() - startAt); }
特に setTimeout は最高ですね…
キャンセルする
キャンセルはAbortControllerを使ってできます。
import { setTimeout } from 'timers/promises' const controller = new AbortController(); (async() => { console.log('start'); await setTimeout(1000, null, { signal: controller.signal }); console.log('end'); })().catch(()=>console.log('aborted')); // 上の setTimeout が発火する前にキャンセルしてみる await setTimeout(500); controller.abort();
AbortController ということは React の useEffect とも相性が良いです。
useEffect(() => { const controller = new AbortController(); const { signal } = controller; (async () => { const res = await fetch('http://example.com', { signal }); await setTimeout(1000, null, { signal }); console.log(res); })(); return () => controller.abort(); });
clearTimeout を使うよりグッとシンプルになりました。
timers/promises の 型定義
型定義がまだなさそうだったので自分で書きました。おそらく合ってますが間違えている可能性はあります。
module 'timers/promises' { const setTimeout: <T>( delay?: number, value?: T, options?: { ref?: boolean; signal?: AbortSignal } ) => Promise<T>; const setImmediate: <T>( value?: T, options?: { ref?: boolean; signal?: AbortSignal } ) => Promise<T>; const setInterval: <T>( delay?: number, value?: T, options?: { ref?: boolean; signal?: AbortSignal } ) => AsyncIterable<T>; }
debounce や throttle を実装してみる
試しに debounce や throttle を実装してみました。Promisify されていない setTimeout を使った方がわかりやすいかもしれないです。
import { setTimeout } from 'timers/promises'; let controller: null | AbortController = null; export const debounce = <T extends unknown[]>( f: (...args: T) => void, wait: number ) => async (...args: T) => { if (controller) controller.abort(); controller = new AbortController(); setTimeout(wait, null, controller) .then(() => f(...args)) .catch(() => {}); };
import { setTimeout } from 'timers/promises'; let timer: null | Promise<unknown> = null; export const throttle = <T extends unknown[]>( f: (...args: T) => void, wait: number ) => async (...args: T) => { if (timer == null) { f(...args); await (timer = setTimeout(wait)); timer = null; } };