After some reading and watching stuff, I think this is about right: Sync (call): Stop what you are doing and do this, then return to continue here. Async (await call): Stop what you are doing and do this, continue here sometime later. Task (MessageChannel): Do this soon-ish, but I don't care when. Task (setTimeout): Do this soon-ish after this much time elapsed. Microtask (queueMicrotask): Do this as soon as you are free, even if you have to block rendering to do it. Microtask (Promise-resolve-then): use queueMicrotask if available (which currently is everywhere). requestAnimationFrame: Do this just before the next rendering. setImmediate: Browser: Avoid, not portable, not standard. Use Task (or requestAnimationFrame if it makes sense). Node: Do this sometime before processing more requests. Nanotask (process.nextTick): Avoid, not portable. You probably want Microtask (you may need process.nextTick because you need ordering with other code that use it). Hot take: process.nextTick should be deprecated in favor of queueMicrotask.
I fired up IE11 to test this. In IE11 (after ES5ifying the code), setImmediate callbacks run before setTimeout tasks (as with Node), but MessageChannel tasks run before setImmediate callbacks.
It schedules a callback just before the next paint will happen in the browser. It schedules that function as a microtask at that point in time, but it’s not “the next” microtask.
@@dassurma raising the question of when the next paint is. I assumed it was normally simply after the last vsync, but it would be interesting to know how it deals with modern high and variable frame rate monitors. In particular, VRR means "after the last refresh" is as soon as you submit the image to the swap chain, so you could in theory starve the task queues if RAF got to (literally?) jump the queue. I imagine this was already hit with driver forced disabled vsync and solved by modern browser async rendering, though scheduling fairness probably is still a real problem.
Is the use of MessageChannel a standard way or just some sort of "hack"? Because it's the first time I came across this and I was always under the assumption setTimeout is the "idiomatic" way of scheduling task. As for setImmediate, I think you got it right because I got similar results in small experiments I ran to verify the scheduling some time ago (because of a notorious bug I introduced in one of my active codebase while trying to be clever), although the ordering how it was affected by the use of MessageChannel is very confusing. Anyways, as always, thanks for the great content.
Great breakdown of the different task scheduling functions! It would have been really interesting if you discussed use cases for when one might prefer using a microtask over a task. For now, I just know that `queueMicrotask()` will happen first but not sure when I would use that knowledge.
I have used setImmediate before and the way I looked at it as it was a replacement of setTimeout with 0. In other words, I felt it is the same thing as setTimeout with 0 time and looked a bit cleaner to read and understand.
This is correct. Back in the day when only `setTimeout` existed (no `Promise`, etc), `setImmediate` was introduced as a clean/correct way for `setTimeout` with 0, which was considered a workaround.
After the poll phase is empty, the next phase is to check if there is any setImmediate on the queue and after that, the timers are executed. So, I guess that is why you get that result at 14:49
I suspect it's defensive programming (and being explicit). Search for "Jake Archibald: Don't use functions as callbacks unless they're designed for it" > ['1.2', '2.3', '10.11'].map(parseInt); (3) [1, NaN, 2]
Please, may you explain why microTasks are "function call dependant" and not macroTasks as I understand from this test: function tasksFromFunctionCall() { setTimeout(() => { console.log("Came from setTimeout [macroTasks]"); }, 2000); queueMicrotask(() => { Array(40000000) .fill(5) .map((e) => e + 5); console.log("Came from queueMicrotask [microTask]"); }); console.log("Came from inside function body"); return "Came from the Return function"; } tasksFromFunctionCall() ******* Result ******* Came from inside function body Came from queueMicrotask [microTask] "Came from the Return function" Came from setTimeout [macroTasks]
That isn't the right order. You're being misled by when the devtools console displays the return value of a function jsbin.com/nujusinaja/edit?js,console
@@jakearchibald Indeed, thank you. Does this mean anything special about "the devtools console displays" for function returns? There is no specific order (rules) for this? I mean, is this devtools print "
@@benoitleger-derville6986 devtools will execute the code you give it, then output the return value. Executing the code ends with the JS stack emptying, which causes microtasks to be processed, so the microtasks run before the return value is displayed. The confusion happens because the return value display isn't part of the console log queue. Even in the console if you log the return value (rather than just letting devtools display it) you'll see the correct order
Surma: "setImmediate is something that existed in the browser, but only IE, no other browser shipped it." Me: "So what you're saying is, it never existed." I try my hardest to ignore anything that IE ever did against standards, and pretend that Microsoft first got into the browser game with Edge.
According to caniuse this existed in early Edge but was removed. Or rather, I should say, looking at the version numbers, it was not ported to Chromium.
The setTimeout-example looks a little "cargo-culty" with the callback wrapped in a lambda for no apparent reason. I (hopefully) imagine it's compiled away as to not make a difference, but surely the more direct setTimeout(cb, 0) is preferable for readability here? I refuse to believe a redundant anonymous function scope could possibly make a difference for event scheduling (well knock on wood, it's JavaScript).
@Ricardo Ferreira Sure, but the given example doesn't feature any arguments to the callback. It just seems needlessly verbose for the given example (although setTimeout is discouraged for this purpose).
@Ricardo Ferreira you can pass arguments directly to setTimeout after the delay parameter and they will be passed to the callback function when it is called. E.g. setTimeout(cb, 0, arg1, arg2)
@@JamesCoyle95 ofc there are some special cases where you'll need to contain certain parameters within a closure, eg. if setTimeout schedules a function call from inside a loop, passing some value dependent on the index. But I reckon that's well beside your point, comment mosdef not intended as some "gotcha"! Not to mention, anyone going about triggering async timeouts in a tight loop should probably seriously consider if it's actually what they want to be doing, maybe not the best example.
If it's a short callback, having it inline is more readable than having to scroll (or even, go to another file) to see the function content. Also: jakearchibald.com/2021/function-callback-risks/
When you want to run some code, but you want to let go of the main for a bit so it can run other stuff. For example let the browser do layout and run some code after. One use case is like I have an api that you can call like drawCircle drawRectangle, but I only do the drawing on the next frame, so I everything at once after your code finishes running.
Would be useful if there was an API that you could “see” what is scheduled in each queue. Without that and or clear guideline on what needs to go where, we are just playing the highest Z-index game again. They will keep adding higher and higher priority queues... scheduleMicroTickMoreImmediatest(bc)
If the queue was mutable, you'd still end up with the z-index game, where everyone things their thing is 'most important', so they put it ahead of everything else.
@@jakearchibald where do we go about making such proposal? The ChromeDevTools GitHub org doesn’t seems to be open to public discussion, and I’m not sure if crbug.com is a suitable place.
Trees/paper are infinitely more renewable, recyclable, and energy efficient than computers. There are more trees in the US and Europe than in 1900. The line was said with good intentions but it’s not meaningful.