たとえば、Date.now()
をsetTimeout(cb, 1)で遅らせながら繰り返し実行したとき、それぞれは1msずつズレているべきであるだろう。ただし実際はそんなことはない。
以下の非同期関数を使用し検証してみた。
- setTimeout(cb, delay)
- delay = 0
- delay = 1
- delay = 2
- process.nextTick
- setImmediate
コードを以下に示す。
co = require 'co'
run = (func)->
ts = []
for i in [1..2000]
ts.push Date.now()
yield new Promise func
x = 0
y = 0
for i, t of ts
if i > 0
d = ts[i] - ts[i-1]
y = y + d / 2000
if d is 0
x = x + 1
console.log x, y
co ->
yield run (r)-> setTimeout r, 0
yield run (r)-> setTimeout r, 1
yield run (r)-> setTimeout r, 2
yield run (r)-> process.nextTick r
yield run (r)-> setImmediate r
結果は
32 1.1624999999999441
51 1.138499999999946
0 2.1484999999999115
1984 0.008000000000000002
1967 0.01600000000000001
となった。一つ目の整数が「何回前回と同じUNIX TIMEになったか」で、2つめの少数が「各コールバック間の平均UNIX TIME差」である。
つまるところ
- setTimeoutの第二引数には0を与えたとしてもコールバックの実行は1ms後になる。 node.jsで単に次回のイベントループで実行したいばあいは
setImmediate
もしくはprocess.nextTick
を使うべきである - setTimeoutのdelayが1ms(および0ms)では、呼び出し時とコールバック時で
Date.now()
が、呼び出し時とコールバック実行時に同一になってしまう可能性がある(厳密に1ms経たずにコールバックが実行されてしまうことがある)。常にユニークなUNIX TIMEを得るためには少なくともsetTimeout(cb, 2)
のように2ms待機する必要がある
という結果となった。
process.nextTickとsetImmediateの違い
イベントループ毎に一回呼ばれるのは同じで時間的に差はないのだが、タイミングが少し違う。下記リンクが詳しい。