素人がプログラミングを勉強していたブログ

プログラミング、セキュリティ、英語、Webなどのブログ since 2008

連絡先: すかいぷ:javascripter_  か javascripter あっと tsukkun.net skypeのほうがいいです

MutationObserverを使った高速setImmediate/nextTick

MessageChannel / setTimeout / requestAnimationFrame / postMessageを使ったものより異様に速い。 というのも、setTimeout等は、次のサイクル(すなわちnextTick)の開始時に実行する関数を登録し、DOMや画面の更新後に実行されるのだが、 MutationObserverのコールバックは、現在の(同期的な)JS実行が完了した時点でコードを走らせるためである。さらに、MutationObserverは同じ実行サイクルでのDOM上の変更を次回のサイクルにすべてまとめてrecordsとしてコールバックに渡すので、これもsetImmediateの実装には非常に都合が良い。

MutationObserverのような実行タイミングをmicrotaskといい、setTimeout / postMessage / MessageChannelなどの実行タイミングをmacrotaskというらしい。

div.id = 'a';
div.id = 'b';
var setImmediate;
var clearImmediate;

(function () {

  var id = 0;

  var div = document.createElement('div');

  var queue = [];

  var observer = new MutationObserver(function (records) {

    queue.forEach(function (opts) {
      var callback = opts.args[0];
      var params = Array.prototype.slice.call(opts.args, 1);
      callback.apply(null, params);
    });

    // clear the queue
    queue.length = 0;
  });

  observer.observe(div, { attributes: true });

  setImmediate = function () {
    queue.push({
      id: ++id,
      args: arguments
    });
    div.id = Math.random();
    return id;
  };

  clearImmediate = function (id) {
    var i = queue.length;
    while (i--) {
      if (queue[i].id == id) {
        queue.splice(i, 1);
      }
    }
  };

})();



setImmediate(console.log.bind(console, 2));
setImmediate(console.log.bind(console, 3));

clearImmediate(setImmediate(console.log.bind(console, 0)));

console.log(1);

/*
1
2
3
*/

参考: Twitter