endaaman.com

2015-11-24

Tips

node.jsにおけるsetTimeoutの精度について

たとえば、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差」である。

つまるところ

  1. setTimeoutの第二引数には0を与えたとしてもコールバックの実行は1ms後になる。 node.jsで単に次回のイベントループで実行したいばあいはsetImmediateもしくはprocess.nextTickを使うべきである
  2. setTimeoutのdelayが1ms(および0ms)では、呼び出し時とコールバック時でDate.now()が、呼び出し時とコールバック実行時に同一になってしまう可能性がある(厳密に1ms経たずにコールバックが実行されてしまうことがある)。常にユニークなUNIX TIMEを得るためには少なくともsetTimeout(cb, 2)のように2ms待機する必要がある

という結果となった。

process.nextTickとsetImmediateの違い

イベントループ毎に一回呼ばれるのは同じで時間的に差はないのだが、タイミングが少し違う。下記リンクが詳しい。


©2024 endaaman.com