8

Deferring the execution of functions, for example in custom event handling, is a common pattern in JavaScript (see, for example here). It used to be that using setTimeout(myFunc,0) was the only way to do this, however with promises there is now an alternative: Promise.resolve().then(myFunc).

I had assumed that these would pretty much do the same thing, but while working on a library which included custom events I thought I'd find out if there was a difference, so I dropped the following block into node:

var logfn=function(v){return function(){console.log(v)}};

setTimeout(logfn(1),0);
Promise.resolve().then(logfn(2));
logfn(3)();

I was expecting to see on the console 3, 1, 2 but instead I saw 3, 2, 1. So in other words the Promise is NOT equivalent to using setTimeout and comes out of the blocks first. At least in Node.

I repeated the test in Chrome and Firefox with the same result, however in Edge it came out as 3, 1, 2. I would also expect non-native promise libraries to use setTimeout under the hood so would come out the same as Edge.

What determines the order of these calls being resolved? What model is used by these different environments to determine execution order? Does any of the above represent standard or non-standard behaviour?

PS I'm defniately not suggesting relying on any of this staying consistant, I'm just curious.


After the answer given below pointed me in the right direction, and as mentioned briefly in the comment below, I found the complete answer in an excellent article by Jake Archibald (with an example almost identical to my code above) which I though I'd add up here rather than leaving it buried in a comment.

Euan Smith
  • 2,102
  • 1
  • 16
  • 26
  • FWIW, nothing is *guaranteeing* any particular order for asynchronous execution. You should never try to rely on execution order in the first place. Which makes this question an interesting discussion about implementation details, but practically moot. – deceze Apr 20 '16 at 09:08
  • Maybe, but I tend to find that my coding improves the more I understand how it all hangs together, even if is indirect. After digging around the answer below I also found almost exactly my example above plus a full explanation [here](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/). Given all of that I can definitely see cases where pushing onto the microtask or macrotask queue specifically could be appropriate, although being mindful that you need to ensure that nothing is broken when only macrotasks are available – Euan Smith Apr 20 '16 at 09:35

1 Answers1

2

ll depends how resolve() was internally implemented - probably you observe difference between setTimeout(fn, 0) and Edge implementations for setImmediate(fn)

Please consider the article - http://www.mattgreer.org/articles/promises-in-wicked-detail/ and the way how resolve method was implemented.

function resolve(value) {
    // force callback to be called in the next
    // iteration of the event loop, giving
    // callback a chance to be set by then()
    setTimeout(function() {
        callback(value);
    }, 1);
}

Than some explenations can be found at priority between setTimeout and setImmediate

From Microsoft documentation - https://developer.microsoft.com/en-us/microsoft-edge/platform/documentation/dev-guide/performance/efficient-script-yielding/ and one more link - setImmediate method

Community
  • 1
  • 1
Krzysztof Safjanowski
  • 7,292
  • 3
  • 35
  • 47
  • 2
    Thank you for the article link and the reference to `setImmediate` which was new to me. Looking that up led me to the polyfill [setImmediate](https://github.com/YuzuJS/setImmediate) which led to this previous stackoverflow answer on the difference between the [microtask and macrotask event queue](http://stackoverflow.com/a/25933985/4098951) which seems to explain what was happening. I guess Chrome et al uses the microtask event queue to implement promises with 'setTimeout' going on the macrotask queue. Edge, perhaps, makes no such distinction. – Euan Smith Apr 20 '16 at 09:21