问题
From my global understanding of how javascript virtual machines works, i can clearly see that the concept of micro task / macro task play a big role.
Here is what i understand about that:
- A VM 'turn' is the fact of pulling ONE macro task out of the VM macro task queue, and execute it.
- During a VM turn, micro tasks can be added to the micro tasks queue of the current macro task.
- Micro tasks can push other micro tasks to the micro tasks queue of the current macro task.
- A VM turn will end when the micro task queue is empty.
And here is the point of my question:
Why there is no clear API to manipulate those two queues.
Something like
pushToMacroTask( function )
pushToMicroTask( function )
Actually it seams like the only way to manipulate those queue is to use setTimeout()
to add tasks to the macro tasks queue and Promises
to add tasks to the micro tasks queue...
I'm ok with that but this does not give us a meaningfull API, don't you think ?
Is this concept supposed to remain 'hidden' to JS dev and only used in some hacky situations ?
Do you know if there is any W3C spec around that subject ?
Does all VM engines implement this concept the same way ?
I'd be glad to ear stories and opinions about that.
Thanks !
回答1:
Is there is any W3C spec concerning micro/macro tasks?
W3C speaks of task queues:
When a user agent is to queue a task, it must add the given task to one of the task queues of the relevant event loop. All the tasks from one particular task source (e.g. the callbacks generated by timers, the events dispatched for mouse movements, the tasks queued for the parser) must always be added to the same task queue, but tasks from different task sources may be placed in different task queues.
EcmaScript2015 speaks of Job Queues, and requires that at least two are supported:
- ScriptJobs: Jobs that validate and evaluate ECMAScript Script and Module source text.
- PromiseJobs: Jobs that are responses to the settlement of a Promise.
This language definition is ignorant of a possible event loop, but one can imagine one or more Job Queues being reserved for use with the Task Queues mentioned in the W3C specs. A browser will trigger the setTimeout
callback according to the W3C Task Queue specification -- linked to a Job Queue --, while a promise must use the Job Queue specification directly (not the Task Queue). That an agent could inject tasks into a Job Queue is mentioned as well:
Alternatively, [an implementation] might choose to wait for a some implementation specific agent or mechanism to enqueue new PendingJob requests.
The EcmaScript specs do not enforce a priority for servicing different Job Queues:
This specification does not define the order in which multiple Job Queues are serviced. An ECMAScript implementation may interweave the FIFO evaluation of the PendingJob records of a Job Queue with the evaluation of the PendingJob records of one or more other Job Queues.
So, there seems no strict requirement here that promise fulfilments should be serviced before setTimeout
tasks. But the Web Hypertext Application Technology Working Group is more specific when covering event loops:
Each event loop has a microtask queue. A microtask is a task that is originally to be queued on the microtask queue rather than a task queue.
Do all VM engines implement this the same way?
This article might be an interesting read that shows how different browser's implementations lead to different orders of execution:
Some browsers [...] are running promise callbacks after
setTimeout
. It's likely that they're calling promise callbacks as part of a new task rather than as a microtask.Firefox and Safari are correctly exhausting the microtask queue between click listeners, as shown by the mutation callbacks, but promises appear to be queued differently. [...] With Edge we've already seen it queue promises incorrectly, but it also fails to exhaust the microtask queue between click listeners, instead it does so after calling all listeners.
This article was written in August 2015, and since then several issues have been solved and harmonised.
Note however that there does not have to be one micro task queue, nor one macro task queue. There can be several queues, each with their own priority.
A meaningful API
It is of course not so difficult to implement the two functions you suggested:
let pushToMicroTask = f => Promise.resolve().then(f);
let pushToMacroTask = f => setTimeout(f);
let say = msg => console.log(msg);
pushToMacroTask(say.bind(null, 'Macro task runs last'));
pushToMicroTask(say.bind(null, 'Micro task runs first'));
来源:https://stackoverflow.com/questions/41075724/javascript-api-to-explicitly-add-micro-tasks-or-macro-tasks