2015.01.09

is Promise.prototype.then the fastest “nextTick”?

I spent the past week optimizing the startup performance of a Firefox OS web app. While analyzing the profile and timeline data I realized that the DOMEvent “message” was causing a considerable delay – That “message” is actually based on David Baron’s setZeroTimeout function, which is used a few times along the codebase; So I had the idea to use a resolved Promise and check if it was going to change anything and I was impressed with the performance gains! So I decided to search if anyone else got into the same results, but couldn’t find anything and decided to write this post.

Promise.prototype.then calls the callback during the microtask queue which happens at the end of the run loop…

Promise.resolve().then(() => console.log('log: 1'));
console.log('log: 2');
// > "log: 2"
// > "log: 1"

So it means that the callback will probably happen before any paint/reflow/style that might be triggered during that loop (unless you force a reflow…), so that is the main reason why it improved the performance of my app, it basically batched all the reflows and code execution, reducing layout thrashing and doing more things in parallel, besides the fact that it executes way faster than the window.postMessage.

Here is the extremely simple code:

// insanely fast nextTick for browsers that support native Promises!
var resolved = Promise.resolve();
module.exports = function nextTick(fn) {
  resolved.then(fn);
};

If you want to compare with other crazy solutions, see this jsperf test.

I did not test the memory usage – theoretically will cause more garbage collection since Promise#then returns a new Promise object at each call; but the performance gains and the amount of nextTick calls that I have (less than 10 calls during startup) should render that irrelevant.

I’m not sure if this is a good idea or not, but I know it improved my app startup and things still work, so it’s good enough for me.

“the best way to get the right answer on the Internet is not to ask a question, it’s to post the wrong answer.” – Cunningham’s Law

PS: I’m trying to make a complex web app load under 1s on a low-end mobile device; on a desktop browser the performance difference is negligible in most cases. (this change alone saved me ~80ms)

PPS: to understand why you might need a nextTick function, see the node.js documentation

Further Reading


Comments

Very interesting!

It would be nice if Firefox and other browsers support setImmediate: https://developer.mozilla.org/pt-BR/docs/Web/API/Window.setImmediate

In the mean time, the setImmediate polyfill (https://github.com/YuzuJS/setImmediate) could use your trick with Promises

I found some people on bugzilla talking about this Promise trick on the ticket about setImmediate:

https://bugzilla.mozilla.org/show_bug.cgi?id=686201#c68

It is indeed fast and lean. The comparable solution would be a MutationObserver based nextTick, which is a tiny bit slower and a bit more code, and with slightly better support (IE11 & Android 4.4): http://jsperf.com/test002/11

Diego Medeiros Vilar Oliveira

This will end up being a pick-a-strategy-sniffing-browser solution, Miller. MessageChannel turned out to be way faster on iOS [8].

Leave a Comment

Please post only comments that will add value to the content of this page. Please read the about page to understand the objective of this blog and the way I think about it. Thanks.

Comments are parsed as Markdown and you can use basic HTML tags (a, blockquote, cite, code, del, em, strong) but some code may be striped if not escaped (specially PHP and HTML tags that aren't on the list). Line and paragraph breaks automatic. Code blocks should be indented with 4 spaces.