読者です 読者をやめる 読者になる 読者になる

JavaScriptで関数を非同期呼び出しする

よくあるパターン。

setTimeout(function(){
  // 非同期でしたい処理
}, 0);

これ結構パフォーマンス悪いみたい。

node.jsの場合

process.nextTick(function(){
  // 非同期でしたい処理
});

最新のIEの場合

setImmediate というAPIが使えるらしいからそれを使う。

setImmediate(function(){
  // 非同期でしたい処理
});

それ以外のブラウザ

MessageChannel を使うのがいいっぽい。

var ch = new MessageChannel();
ch.port1.onmessage = function(){
  // 非同期でしたい処理
};
ch.port2.postMessage(0);

まとめ

だいたいこんな感じ。
uncurryThis はこの記事で知りました。

var asyncCall = function(){
  var _slice = Array.prototype.slice;
  var _bind = Function.prototype.bind || function(thisObject){
    var callee = this;
    var args = _slice.call(arguments, 1);
    return function(){
      var a = _slice.call(arguments);
      return callee.apply(thisObject, args.concat(a));
    };
  };

  var uncurryThis = _bind.call(_bind, Function.prototype.call);
  var slice = uncurryThis(_slice),
    bind = uncurryThis(_bind),
    apply = uncurryThis(Function.prototype.apply);

  return typeof process !== 'undefined'? function(){
    var args = slice(arguments);
    process.nextTick(apply(bind, null, args));
  }: typeof setImmediate === 'function'? function(){
    var args = slice(arguments);
    setImmediate(apply(bind, null, args));
  }: typeof MessageChannel === 'function'? function(){
    var ch = new MessageChannel(), queue = [];
    ch.port1.onmessage = function(){ queue.shift()(); };
    return function(){
      var args = slice(arguments);
      queue.push(apply(bind, null, args));
      ch.port2.postMessage(0);
    };
  }(): function(){
    var args = slice(arguments);
    setTimeout(apply(bind, null, args), 0);
  };
}();

第1引数に非同期で呼び出したい関数、第2引数にthisになるオブジェクト、第3引数以降に引数を指定する。

asyncCall(function(a, b, c){
  this.valueOf(); // 0
  a; // 1
  b; // 2
  c; // 3
}, 0, 1, 2, 3);

のように使える。