setImmediate: a must-have polyfill

Perry Mitchell / 2016-07-06 23:16:59
setImmediate: a must-have polyfill

setTimeout and setInterval are two highly used asyn­chro­nous meth­ods avail­able in JavaScript that help sched­ule func­tion ex­e­cu­tion. Few peo­ple are aware that there was al­most a third method in this tim­ing group, called setImmediate.

setImmediate fires a call­back method in an­other ex­e­cu­tion con­text, but with a higher pri­or­ity than that of setTimeout or setInterval with a de­lay time of 0. setImmediate was pro­posed by Microsoft and was first im­ple­mented in Internet Explorer 10. It’s also avail­able in NodeJS, but not any other browsers. The na­tive (IE) setImmediate is a long way ahead of other timers in terms of per­for­mance:

JavaScript yielding with timers and setTimeout

When call­ing setImmediate, no de­lay time is nec­es­sary:

setTimeout(myFunction, 0);

setImmediate(myFunction);

setImmediate calls take a higher pri­or­ity over setTimeout, so it’s ideal to use in per­for­mance crit­i­cal call­back sched­ul­ing. Several of the bet­ter Promise poly­fills use setImmediate for their call­back sched­ul­ing:

// Use polyfill for setImmediate for performance gains
var asap = (typeof setImmediate === 'function' && setImmediate) ||
  function (fn) {
    setTimeoutFunc(fn, 0);
  };

Being that setImmediate is not sup­ported by any other browser other than Internet Explorer, you need to use a poly­fill. The poly­fill has some in­ter­est­ing meth­ods at its dis­posal to mimic the high-pri­or­ity na­ture of its na­tive coun­ter­part:

  • process.nextTick() - When run­ning un­der Node, YuzuJS’ setImmediate can use the built-in nextTick method to em­u­late the func­tion.
  • window.postMessage() - Browsers don’t usu­ally have the process global, so the postMessage is the most com­monly used poly­fill for setImmediate. Window mes­sages have a very high pri­or­ity call­back sta­tus, so they’re quite ideal for this pur­pose, but there are a num­ber of caveats here which will be men­tioned later.
  • MessageChannel - Browsers that sup­port MessageChannels can poly­fill setImmediate by cre­at­ing 2 dummy chan­nels and fir­ing an event (message) from one to the other.
  • onreadystatechange() - Older browsers that don’t sup­port the above meth­ods will use the onreadystatechange call­back for new scripts be­ing ex­e­cuted.
  • setTimeout - If none of the above are sup­ported (why would we want to run here?), setTimeout(fn, 0) can be used as a low-per­for­mance al­ter­na­tive.

As a high-per­for­mance async tool, setImmediate is a great ad­di­tion to cer­tain ap­pli­ca­tions. In some cases it could be seen as a some­what ag­gres­sive tac­tic to call­backs, as (for in­stance) the postMessage im­ple­men­ta­tion will take pri­or­ity over all time­outs ini­ti­ated by calls to setTimeout. Other JavaScript ap­pli­ca­tions will have less pro­cess­ing time when a script us­ing se­tIm­me­di­ate is mak­ing re­peated calls us­ing the postMessage im­ple­men­ta­tion.

In Firefox 3+, Internet Explorer 9+, all mod­ern WebKit browsers, and Opera 9.5+, postMes­sage is avail­able and pro­vides a good way to queue tasks on the event loop. It’s quite the abuse, us­ing a cross-doc­u­ment mes­sag­ing pro­to­col within the same doc­u­ment sim­ply to get ac­cess to the event loop task queue, but un­til there are na­tive im­ple­men­ta­tions, this is the best op­tion. (YuzuJS’ se­tIm­me­di­ate poly­fill)

I’ve used setImmediate to speed up my Promise poly­fills in sev­eral pro­jects. It’s def­i­nitely safe for use, but be weary us­ing on pages that have a sub­stan­tial amount of code run­ning un­der win­dow message event lis­ten­ers.