名前から既にワクワクするこの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'));
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;
}
};