LoginSignup
14
14

More than 5 years have passed since last update.

Backbone.jsにcron機能を実装する30行のCoffeeScript

Last updated at Posted at 2013-06-25

まあコンパイルしてJavaScriptに展開すると100行超えるんですが。

もうちょい徹底すれば25行くらいにはなりそうだけど、可読性を失う自己満足になるのでこの辺で自重。

Backbone.Cronクラス
class Backbone.Cron
  constructor: (@owner, crontabs, autostart = true) ->
    seqArray = (from, to) -> _.map(new Array(to - from + 1).join().split(','), (v, i, a) -> i + from)
    parseCrontab = (crontab) =>
      ctArray = _.map(crontab.split(/\s+/), (v, i, a) ->
        return v if i is 6 or v is '*'
        v = v.replace /(\d+)-(\d+)/g, (vm, vf, vt) -> seqArray(Number(vf), Number(vt)).join()
        v = v.replace /\*\/(\d+)/g, (vm, vi) -> Number(vi) * -1
        _.map(v.split(','), (vv, vi, va) -> Number vv).sort())
      unit: @_makeunit ctArray
      exec: ctArray[6]

    @items = {}
    @items[label] = _.extend parseCrontab(ct), { crontab: ct, pause: false } for label, ct of crontabs
    @owner.bind 'remove', () => @stop() unless @owner.model?
    @start() if autostart

  start: () =>
    @unixtime = parseInt(new Date() / 1000, 10)
    @timer = setInterval () =>
      d = new Date(++@unixtime * 1000)
      now = @_makeunit [ d.getSeconds(), d.getMinutes(), d.getHours(), d.getDate(), d.getMonth() + 1, d.getDay() ]
      for label, item of @items when not item.pause
        @owner[item.exec] label, d if _.every item.unit, (v, k ,o) -> v? and (v is '*' or _.some(v, (vv, vi, va) -> if vv < 0 then now[k] % (vv * -1) is 0 else vv is now[k]))
    , 1000

  stop: () => clearInterval @timer
  on: (label) => @items[label].pause = false
  off: (label) => @items[label].pause = true
  _makeunit: (array) => _.object [ 'second', 'minute', 'hour', 'day', 'month', 'weekday' ], array
  • デモ付きのソースをGitHubにも上げてあります。
  • デモはこちら

使い方

まず上記ソースをコピペしてJavaScriptにコンパイルしたファイルをbackbone.jsの後にロードしてください。

<script type="text/javascript" src="/path/to/backbone.js"></script>
<script type="text/javascript" src="<上記ソースをコピペしてjsにコンパイルしたファイル>"></script>

Backbone.~.extendinitialize()でBackbone.Cronオブジェクトを作成します。

モデルにcronを仕込む例
var HogeModel = Backbone.Model.extend({
  initialize: function () {
    this.cron = new Backbone.Cron(this, {
      test1: '0,45,20-23 * * * * * test1',
      test2: '39 */6,10,20,40,50 * * * * test2',
      test3: '0 */5 */2 * * * test2'
    });
  },

  test1: function (label, now) {
    console.log(label, now);
  },

  test2: function (label, now) {
    console.log(label, now);
  }
});

メソッド

new Backbone.Cron(<関連付けるオブジェクト>, <crontab設定>[, 自動スタート設定]);

引数1: 関連付けるオブジェクト

引数2のcrontabで指定したメソッドを実行させるオブジェクトです。大抵の場合はthisで良いと思います(上記の例ではthis = HogeModelオブジェクト)。

引数2: crontab設定

ラベル:crontabのペアからなる連想配列を指定します。crontabは

曜日 実行するメソッド

の順に半角スペース区切りで指定します。一通りのcrontab書式が指定可能です。

ここで指定したメソッドが呼び出される時には(ラベル現在日時のDateオブジェクト)の2つが引数で渡されます。

引数3: 自動スタート設定

デフォルトではBackbone.Cronをnewした時点でタイマーが作動します。とりあえずnewはするけど実行はちょっと後でという場合にはfalseを指定してください。そして必要になったらstart()メソッドでタイマーを作動させてください。

start()

作成したBackbone.Cronオブジェクトのタイマーを作動させます。

stop()

Backbone.Cronオブジェクトのタイマーを停止させます。crontabで登録した全ての処理がストップされます。

off(<ラベル>)

指定したラベルの処理だけ実行させないようにします。

on(<ラベル>)

off()で止めたラベルの処理を再度実行させるようにします。

注意点

Backbone.Cronを仕込んだオブジェクトを削除する前には必ずstop()メソッドでタイマーを止めてください。そうしないとオブジェクトを削除した後にもタイマー処理が動き続け、メモリリークが発生します。

なお、Backbone.Cronの内部では、removeイベントでstop()が発生するようにbindしているので、Backbone.Modelに関連付けられたBackbone.Cronオブジェクトに関しては、Model削除時に自動的にタイマーが停止されます。

ただし、Collection.reset()の場合、(Backbone.jsバージョン1.0.0の段階では)Modelに対してremoveイベントが発生しないのでタイマーが停止されません。そこで、Backbone.Collection.extendでreset()の処理を以下の様に拡張しておけば、reset()時にremoveイベントが発火されるようになります。

Backbone.Collectionのreset()を拡張
var HogeCollection = Backbone.Collection.extend({
  model: HogeModel,

  reset: function (models, options) {
    this.each(function (model) {
      model.trigger('remove');
    });
    return HogeCollection.__super__.reset.apply(this, arguments);
  },

最後に

Backbone.jsに特化しているのはremoveイベントのbindだけなので、この部分を潰せばBackbone.jsに限らず、色々なオブジェクトにCron処理を実装する事ができます(underscore.js依存ですが)。

ちなみにモダンブラウザとIE7以上で動作確認済みです。

14
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
14