javascriptで発生するイベントを間引く
近辺で話題になっていたのでまとめた。
イベントを律儀に全て処理しているとattachした即時関数が過剰に実行されてしまう。API通信が大量に発生したり、処理落ちが発生したりする。
lodash・underscoreに実装されている_.throttle
・_.debounce
というファクトリを利用することで、これらをスマートに解決できる。
_.throttle
設定した時間内に2回以上実行されない関数を返すファクトリ。スクロールイベントのような連続量を間引くのに利用される。
$el = $ '#header'
($ window).on 'scroll', ->
$el.css top: ($ document).scrollTop()
scrollに同期してヘッダを追従させたりする場合、scrollの全フレームを処理しなければいけないような気になる。
しかし、scrollイベントの全フレームを処理しないとカクカクして見えるほど人類のステージは高くない。
$el = $ '#header'
($ window).on 'scroll', _.throttle (event) ->
$el.css top: ($ document).scrollTop()
, 12
これで「どれだけ実行しても12秒に1回しか実行できないようにラッピングされた」即時関数がscrollにattachされる。
何かをスクロールに同期する場合、12ミリ秒くらいだと殆ど気にならなかった、24くらいまで増やすと結構気になった。
「だいたい41ミリ秒で24fpsだから41でいいじゃん」と思ったけど元のscrollイベントがアナログ値ではないのでそんなことは全く無かった。
入ってきた連続量の頭を評価しない({ leading: false }
)、ケツを評価しない({ trailing: false }
)等のオプションがある。詳しくはドキュメントを参照の事。
_.debounce
実行するとタイマーをリセットしてabort、タイマーが切れると実行される関数を返すファクトリ。
($ '#input').on 'keyup', (event) ->
$el = $ event.currentTarget
$.ajax '/api/search',
data: name: $el.val()
dataType: 'json'
.done (json) ->
$el.val json.name if json.name
こんな即時関数を実装してAPIアクセスが大量に発生し、慌ててsetTimeoutで遅延実行するような機構を実装した経験が誰にでもあると思う。
でもdebounceがあればもう慌てる必要は無い。
($ '#input').on 'keyup', _.debounce (event) ->
$el = $ event.currentTarget
$.ajax '/api/search',
data: name: $el.val()
dataType: 'json'
.done (json) ->
$el.val json.name if json.name
, 240
これで「最後に実行されてから240ミリ秒以内にもう一度実行されたらabort、実行されなければexec」する即時関数がkeyupにattachされる。