setImmediate Test on postMessage (v19)

Revision 19 of this benchmark created on


Description

set immediate test

Preparation HTML

<script>
(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;
}(new Function("return this")()));
    setTimeout(function () {
        if (!window.setImmediate) {
            ui.benchmarks[0].options.defer = false;
        }
        if (!window.msSetImmediate) {
            ui.benchmarks[2].options.defer = false;
        }
        if (!window.MessageChannel) {
            ui.benchmarks[3].options.defer = false;
        } else {
            window.immediateChannel = new MessageChannel();
        }
        if ("onreadystatechange" in document.createElement("script")) {
            window.readyStateChange = true;
        } else {
            ui.benchmarks[4].options.defer = false;
        }

        if (!window.postMessage) {
            ui.benchmarks[5].options.defer = false;
        } else {
            window.immediateCallbacks = {};

            var onGlobalMessage = function (event) {
                if (event.source === window && event.data.indexOf('immediate') === 0) {
                    var handle = 'h' + event.data.substring(9);
                    immediateCallbacks[handle]();
                    delete immediateCallbacks[handle];
                }
            };

            if (window.addEventListener) {
                window.addEventListener("message", onGlobalMessage, false);
            } else {
                window.attachEvent("onmessage", onGlobalMessage);
            }
        }
        window.setImmediateM = (function() {
          var hiddenDiv = document.createElement("div");
          var callbacks = [];
          (new MutationObserver(function(records) {
            var cbList = callbacks.slice();
            callbacks.length = 0;
            cbList.forEach(function(callback) { callback(); });
          })).observe(hiddenDiv, { attributes: true });
        return function setImmediate(callback) {
          if( !callbacks.length) {
            hiddenDiv.setAttribute('yes', 'no');
          }
          callbacks.push(callback);
        };
      })();
        window.setImmediateO = (function() {
          if (!Object.observe) return function(fn) { setTimeout(fn, 0); };
          var dummy = { foo: 0 };
          var callbacks = [];
          Object.observe(dummy, function(records) {
            var cbList = callbacks.slice();
            callbacks.length = 0;
            cbList.forEach(function(callback) { callback(); });
          });
        return function setImmediate(callback) {
          if( !callbacks.length) {
            dummy.foo++;
          }
          callbacks.push(callback);
        };
      })();
        window.setImmediateP = (function() {
          var callbacks = [];
          function run() {
            var cbList = callbacks.slice();
            callbacks.length = 0;
            cbList.forEach(function(callback) { callback(); });
          };
        return function setImmediate(callback) {
          if(!callbacks.length)
            new Promise(function(resolve,reject){resolve();}).then(run);
          callbacks.push(callback);
        };
      })();
    }, 0);

</script>

Test runner

Ready to run.

Testing in
TestOps/sec
setImmediate
// async test
setImmediate(function () {
    deferred.resolve();
});
ready
setTimeout
// async test
setTimeout(function () {
    deferred.resolve();
}, 0);
ready
msSetImmediate
// async test
msSetImmediate(function () {
    deferred.resolve();
});
ready
MessageChannel
// async test
immediateChannel.port1.onmessage = function () {
    deferred.resolve();
};

immediateChannel.port2.postMessage();
ready
readyStateChange
// async test
var scriptEl = document.createElement("script");
scriptEl.onreadystatechange = function () {
    scriptEl.onreadystatechange = null;
    scriptEl.parentNode.removeChild(scriptEl);
    scriptEl = null;

    deferred.resolve();
};

document.documentElement.appendChild(scriptEl);
readyStateChange;
ready
postMessage
// async test
var handle = Math.random()

immediateCallbacks['h' + handle] = function () {
    deferred.thisWillFailSynchronously();
};

postMessage('immediate' + handle, "*");

immediateCallbacks['h' + handle] = function () {
    deferred.resolve();
};
ready
requestAnimationFrame
 
ready
image + data-url
// async test
var i = new Image();

i.onload = function(){ deferred.resolve(); }

i.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
 
ready
MutationObserver
// async test
setImmediateM(function() {
  deferred.resolve();
});
ready
Object.observe
// async test
setImmediateO(function() {
  deferred.resolve();
});
ready
native Promises
// async test
setImmediateP(function() {
  deferred.resolve();
});
ready
setImmediate
// async test
setImmediate(function() {
  deferred.resolve();
});
ready

Revisions

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