0 を指定しても、すぐには動きません。
指定するのは「この時間で動く」ではなく、 この時間が経ったら順番待ちに並ぶという意味です。
いま動いている処理が終わるまで、順番は回ってきません。
これは言語ではなく実行環境の仕組み(HTML Standard)ですが、Node にも同じものが入っています。
動く順番
| 順 | 何が動くか |
|---|---|
| 1 | いま書いてある処理(同期) |
| 2 | Promise の続き(マイクロタスク) |
| 3 | setTimeout で預けたもの(タスク) |
Promise のほうが必ず先です。 0 を指定しても変わりません。
→ Promise — then / catch / finally と async / await
止める
setTimeout() の返り値を取っておいて、clearTimeout() に渡します。
返り値の中身は環境によって違います。 ブラウザでは数値、Node では別の値です。 中身を見たり、比べたり、保存したりしないでください。 渡すだけにします。
引数を渡す
const out = [];
for (var i = 0; i < 3; i++) {
setTimeout(() => out.push(i), 0);
}
await new Promise((resolve) => setTimeout(resolve, 20));
console.log(out.join(' '));第3引数から後ろが、そのまま関数に渡ります。 閉包で渡せるので、いまはあまり使いません。
待つ関数を作る
try {
setTimeout(() => {
throw new Error('中で落ちた');
}, 0);
} catch (error) {
// ここには来ない
}この3行が、非同期の待ち時間でいちばんよく使う形です。 再試行の間隔を空けるときにも使います。 → 失敗したら再試行する
setInterval() — 繰り返す
止めるまで動き続けます。 clearInterval() を必ず書いてください。
中の処理が指定した間隔より長いと、溜まっていきます。
通信のように時間の読めない処理を繰り返すなら、
setTimeout() を終わるたびに掛け直すほうが安全です。
よくある間違い
var で書いたループ
全部 3 になりました。
var はループ全体で1つしかないので、実際に動くころには 3 になっています。
let なら、繰り返しごとに別の入れ物になります。
try で囲んでも捕まらない
try を抜けたずっと後に動くので、この catch は無関係です。
実際に走らせると捕まらないまま外へ出てしまうので、このページでは実行例にできません。
囲むのは中側です。
→ Error と try / catch / finally / throw
指定した時間ちょうどに動くと思う
指定できるのは最短の待ち時間だけです。
- いま動いている処理が終わるまで待たされます
- 画面が裏に回っていると、間隔が延ばされることがあります
- 入れ子にしたタイマーには、下限が設けられています
時間の計測や、正確な間隔が要る処理には使えません。
まとめ
0を指定してもいまの処理が終わってから動く- 順番は 同期 →
Promise→ タイマー - 返り値は
clearTimeout()に渡すだけ。中身は環境で違う sleepはnew Promiseと組にして作るsetInterval()は止めるまで動く。時間の読めない処理には向かないvarのループは全部同じ値になる。letを使う- 外側の
tryでは捕まえられない。 中で囲む - 指定できるのは最短の待ち時間だけ