nexttick

JavaScript performance comparison

Revision 29 of this test case created by

Preparation code


      
      <script>
Benchmark.prototype.setup = function() {
  (function (global, undefined) {

      "use strict";
  
      if (global.setImmediate) {
          return;
      }
  
      var nextHandle = 1; // Spec says greater than zero
      var tasksByHandle = {};
      var currentlyRunningATask = false;
      var doc = global.document;
      var registerImmediate;
  
      function setImmediate(callback) {
        // Callback can either be a function or a string
        if (typeof callback !== "function") {
          callback = new Function("" + callback);
        }
        // Copy function arguments
        var args = new Array(arguments.length - 1);
        for (var i = 0; i < args.length; i++) {
            args[i] = arguments[i + 1];
        }
        // Store and register the task
        var task = { callback: callback, args: args };
        tasksByHandle[nextHandle] = task;
        registerImmediate(nextHandle);
        return nextHandle++;
      }
  
      function clearImmediate(handle) {
          delete tasksByHandle[handle];
      }
  
      function run(task) {
          var callback = task.callback;
          var args = task.args;
          switch (args.length) {
          case 0:
              callback();
              break;
          case 1:
              callback(args[0]);
              break;
          case 2:
              callback(args[0], args[1]);
              break;
          case 3:
              callback(args[0], args[1], args[2]);
              break;
          default:
              callback.apply(undefined, args);
              break;
          }
      }
  
      function runIfPresent(handle) {
          // From the spec: "Wait until any invocations of this algorithm started before this one have completed."
          // So if we're currently running a task, we'll need to delay this invocation.
          if (currentlyRunningATask) {
              // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a
              // "too much recursion" error.
              setTimeout(runIfPresent, 0, handle);
          } else {
              var task = tasksByHandle[handle];
              if (task) {
                  currentlyRunningATask = true;
                  try {
                      run(task);
                  } finally {
                      clearImmediate(handle);
                      currentlyRunningATask = false;
                  }
              }
          }
      }
  
      function installNextTickImplementation() {
          registerImmediate = function(handle) {
              process.nextTick(function () { runIfPresent(handle); });
          };
      }
  
      function canUsePostMessage() {
          // The test against `importScripts` prevents this implementation from being installed inside a web worker,
          // where `global.postMessage` means something completely different and can't be used for this purpose.
          if (global.postMessage && !global.importScripts) {
              var postMessageIsAsynchronous = true;
              var oldOnMessage = global.onmessage;
              global.onmessage = function() {
                  postMessageIsAsynchronous = false;
              };
              global.postMessage("", "*");
              global.onmessage = oldOnMessage;
              return postMessageIsAsynchronous;
          }
      }
  
      function installPostMessageImplementation() {
          // Installs an event handler on `global` for the `message` event: see
          // * https://developer.mozilla.org/en/DOM/window.postMessage
          // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages
  
          var messagePrefix = "setImmediate$" + Math.random() + "$";
          var onGlobalMessage = function(event) {
              if (event.source === global &&
                  typeof event.data === "string" &&
                  event.data.indexOf(messagePrefix) === 0) {
                  runIfPresent(+event.data.slice(messagePrefix.length));
              }
          };
  
          if (global.addEventListener) {
              global.addEventListener("message", onGlobalMessage, false);
          } else {
              global.attachEvent("onmessage", onGlobalMessage);
          }
  
          registerImmediate = function(handle) {
              global.postMessage(messagePrefix + handle, "*");
          };
      }
  
      function installMessageChannelImplementation() {
          var channel = new MessageChannel();
          channel.port1.onmessage = function(event) {
              var handle = event.data;
              runIfPresent(handle);
          };
  
          registerImmediate = function(handle) {
              channel.port2.postMessage(handle);
          };
      }
  
      function installReadyStateChangeImplementation() {
          var html = doc.documentElement;
          registerImmediate = function(handle) {
              // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
              // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
              var script = doc.createElement("script");
              script.onreadystatechange = function () {
                  runIfPresent(handle);
                  script.onreadystatechange = null;
                  html.removeChild(script);
                  script = null;
              };
              html.appendChild(script);
          };
      }
  
      function installSetTimeoutImplementation() {
          registerImmediate = function(handle) {
              setTimeout(runIfPresent, 0, handle);
          };
      }
  
      // If supported, we should attach to the prototype of global, since that is where setTimeout et al. live.
      var attachTo = Object.getPrototypeOf && Object.getPrototypeOf(global);
      attachTo = attachTo && attachTo.setTimeout ? attachTo : global;
  
      // Don't get fooled by e.g. browserify environments.
      if ({}.toString.call(global.process) === "[object process]") {
          // For Node.js before 0.9
          installNextTickImplementation();
  
      } else if (canUsePostMessage()) {
          // For non-IE10 modern browsers
          installPostMessageImplementation();
  
      } else if (global.MessageChannel) {
          // For web workers, where supported
          installMessageChannelImplementation();
  
      } else if (doc && "onreadystatechange" in doc.createElement("script")) {
          // For IE 6–8
          installReadyStateChangeImplementation();
  
      } else {
          // For older browsers
          installSetTimeoutImplementation();
      }
  
      attachTo.setImmediate = setImmediate;
      attachTo.clearImmediate = clearImmediate;
  }(typeof self === "undefined" ? typeof global === "undefined" ? this : global : self));
  
  // END SET IMMEDIATE SHIM
  
  var nextTick2 = function () {
    function nextTick(fn) {
      return setTimeout(fn, 0);
    }
    return nextTick;
  }();
  
  
  var nextTick3 = function () {
    function nextTick(fn) {
      var image = new Image();
      image.onerror = fn;
      image.src = 'data:,foo';
    }
    return nextTick;
  }();
  
  var nextTick4 = function () {
    function nextTick(fn) {
      var script = document.createElement('script');
      script.onload = function() {
        document.body.removeChild(script);
        fn();
      }
      script.src = 'data:text/javascript,';
      document.body.appendChild(script);
    }
    return nextTick;
  }();
  
  var nextTick5 = function () {
  // FAILS ON SOME BROWSERS SO USE SETTIMEOUT INSTEAD
    function nextTick(fn) {
      var req = new XMLHttpRequest;
      req.open('GET','data:text/plain,foo', false);
      req.onreadystatechange = function() {
        req.onreadystatechange = null;
        fn();
      };
      req.send(null);
    }
    return nextTick;
  }();
  
  var nextTick6 = function () {
    var key = 'nextTick__' + Math.random();
    var queue = [];
    window.addEventListener('message', function (e) {
      if (e.data !== key) {
        return;
      }
      queue.shift()();
    },false);
  
    function nextTick(fn) {
      queue.push(fn);
      window.postMessage(key, '*');
    }
  
    return nextTick;
  }();
  
  var nextTick7 = function () {
    function nextTick(fn) {
      requestAnimationFrame(fn);
    }
    return nextTick;
  }();
  
  var nextTick8 = function () {
    var resolved = Promise.resolve();
    function nextTick(fn) {
      resolved.then(fn);
    }
    return nextTick;
  }();
  
  var nextTick9 = function () {
    function nextTick(fn) {
      Promise.resolve().then(fn);
    }
    return nextTick;
  }();
  
  var setImmediate1 = function () {
    function nextTick(fn) {
      setImmediate(fn);
    }
    return nextTick;
  }();

};
</script>

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in CCBot 2.0.0 / Other 0.0.0
Test Ops/sec
window.onmessage
// async test
nextTick6(function() {
  deferred.resolve();
});
pending…
setImmediateShim
// async test
setImmediate1(function() {
  deferred.resolve();
});
pending…
script.onload
// async test
nextTick4(function() {
  deferred.resolve();
});
pending…
setTimeout
// async test
nextTick2(function() {
  deferred.resolve();
});
pending…
requestAnimationFrame
// async test
nextTick7(function() {
  deferred.resolve();
});
pending…
Image.onerror
// async test
nextTick3(function() {
  deferred.resolve();
});
pending…
Promise.prototype.then
// async test
nextTick9(function() {
  deferred.resolve();
});
pending…
Promise.prototype.then (pre-resolved)
// async test
nextTick8(function() {
  deferred.resolve();
});
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL.

0 Comments