nexttick

JavaScript performance comparison

Revision 16 of this test case created by dfgdsdfsfdsdfgq

Preparation code


      
      <script>
Benchmark.prototype.setup = function() {
  var nextTick1 = function() {
    // Firefox doesn't support MessageChannel so we use something that works..
    if (!('MessageChannel' in window)) {
      return function(fn) {
        setTimeout(fn, 0)
      }
    }
    var channel = new MessageChannel();
    var queue = [];
    channel.port1.onmessage = function() {
      queue.shift()();
    };
  
    function nextTick(fn) {
      queue.push(fn);
      channel.port2.postMessage();
    }
    return nextTick;
  }();
  
  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;
  }();
  
  (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 setImmediate;
  
    function addFromSetImmediateArguments(args) {
      tasksByHandle[nextHandle] = partiallyApplied.apply(undefined, args);
      return nextHandle++;
    }
    // This function accepts the same arguments as setImmediate, but
    // returns a function that requires no arguments.
  
    function partiallyApplied(handler) {
      var args = [].slice.call(arguments, 1);
      return function() {
        if (typeof handler === "function") {
          handler.apply(undefined, args);
        } else {
          (new Function("" + handler))();
        }
      };
    }
  
    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(partiallyApplied(runIfPresent, handle), 0);
      } else {
        var task = tasksByHandle[handle];
        if (task) {
          currentlyRunningATask = true;
          try {
            task();
          } finally {
            clearImmediate(handle);
            currentlyRunningATask = false;
          }
        }
      }
    }
  
    function clearImmediate(handle) {
      delete tasksByHandle[handle];
    }
  
    function installNextTickImplementation() {
      setImmediate = function() {
        var handle = addFromSetImmediateArguments(arguments);
        process.nextTick(partiallyApplied(runIfPresent, handle));
        return 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);
      }
      setImmediate = function() {
        var handle = addFromSetImmediateArguments(arguments);
        global.postMessage(messagePrefix + handle, "*");
        return handle;
      };
    }
  
    function installMessageChannelImplementation() {
      var channel = new MessageChannel();
      channel.port1.onmessage = function(event) {
        var handle = event.data;
        runIfPresent(handle);
      };
      setImmediate = function() {
        var handle = addFromSetImmediateArguments(arguments);
        channel.port2.postMessage(handle);
        return handle;
      };
    }
  
    function installReadyStateChangeImplementation() {
      var html = doc.documentElement;
      setImmediate = function() {
        var handle = addFromSetImmediateArguments(arguments);
        // 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);
        return handle;
      };
    }
  
    function installSetTimeoutImplementation() {
      setImmediate = function() {
        var handle = addFromSetImmediateArguments(arguments);
        setTimeout(partiallyApplied(runIfPresent, handle), 0);
        return 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;
    window.setImmediate = setImmediate;
    window.clearImmediate = clearImmediate;
  }(new Function("return this")()));

};
</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
MessageChannel (fallback to setTimeout on Firefox)
// async test
nextTick1(function() {
  deferred.resolve();
});
pending…
setTimeout
// async test
nextTick2(function() {
  deferred.resolve();
});
pending…
Image.onerror
// async test
nextTick3(function() {
  deferred.resolve();
});
pending…
script.onload
// async test
nextTick4(function() {
  deferred.resolve();
});
pending…
window.onmessage
// async test
nextTick6(function() {
  deferred.resolve();
});
pending…
requestAnimationFrame
// async test
nextTick7(function() {
  deferred.resolve();
});
pending…
Promise.prototype.then
// async test
nextTick8(function() {
  deferred.resolve();
});
pending…
setImmediate
// async test
setImmediate(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