結論から言うと、setTimeout関数を実行するにあたり、
diff
1- setTimeout((i) => {
2+ setTimeout(() => {
3 if (i === 13) return onRejected("この数字は不吉です");
4 else if (i > 0) console.log(i);
5 else onFulfilled("Go");
6 }, (num - i) * 1000);
もしくは
diff
1 setTimeout((i) => {
2 if (i === 13) return onRejected("この数字は不吉です");
3 else if (i > 0) console.log(i);
4 else onFulfilled("Go");
5- }, (num - i) * 1000);
6+ }, (num - i) * 1000, i);
のどちらかの修正で期待通りの結果になります。
原因
setTimeoutのコールバック関数に引数がi
ついています。外側の関数にも変数i
があるので非常に紛らわしいですがこのコールバック関数中のi
は外側の関数のものではなく引数のほうに束縛されています。
setTimeout関数はコールバック関数に投入する引数を第3引数以降に記載すると別途指定できます。
https://developer.mozilla.org/ja/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout
質問文のコードではコールバック関数に引数が1つ設定されているにもかかわらず、コールバック関数に渡す値が設定されていません。その場合、引数の値は undefined
となります。
すなわち、コールバック関数内の引数i
は初回実行時の時点でundefined
となり、i === 13
にもi > 0
にもマッチせず、else の文が実行され、即座にPromiseが解決されて終了、となります。
前者の修正の場合
コールバック関数の引数を削除しています。
この場合、コールバック関数内の変数i
は外側の関数の変数i
に束縛されますが、この場合、setTimeout関数コール時点のi
の中身で束縛されることになります。
厳密には異なりますが、外側関数の変数i
の値でコールバック関数内のi
がリテラルのように書き換わるような動作になります。
なので、それぞれi
が3,2,1,0 に置き換わったコールバック関数がスタンバイされることになります。
この場合は、期待通り 3 2 1 Go
と出力されます。
後者の修正の場合
先述したように、ちゃんとコールバック関数に渡す引数の値をdelay値に続いて記載することでi
がundefined
なることを防止できます。
この場合、コールバック関数内引数の変数名は外側のブロックと同じものを使わないようにすべきです。非常に紛らわしく、too bad なコードです。