What is the difference between "event loop queue" and "job queue"?
I can not understand how the following code run. Why "1" is after "b" but "h" is after "3"? Should'n the order be: a, b, 1, 2, h, 3? Some articles said that the difference between "event loop queue" and "job queue" leads to the following output. But how? I have read the specification of ECMAScript 2015 - 8.4 Jobs and Job Queues, wanting to know how Promise'job works, but it makes me more confused. Can someone help me? Thank you!
var promise = new Promise(function(resolve, reject) {resolve(1)});
promise.then(function(resolve) {console.log(1)});
console.log('a');
promise.then(function(resolve) {console.log(2);});
setTimeout(function() {console.log('h')}, 0);
promise.then(function(resolve) {console.log(3)});
console.log('b');
// a
// b
// 1
// 2
// 3
// h
I know Promise is asynchronous, but the callback of setTimeout(..) asynchronous operation is always after Promise's asynchronous operation. Why?
Solution 1:
Why "1" is after "b"?
By promise specification, all promise .then()
handlers are called asynchronously AFTER the current thread of JS has run to completion. Thus, both a
and b
which are executed synchronously as part of the current JS will execute before any .then()
handlers so 1
will always be after a
and b
.
Some interesting reading: Tasks, microtasks, queues and schedules and What is the order of execution in javascript promises and Writing a JavaScript framework - Execution timing, beyond setTimeout.
There's some good advice here in this thread:Promises wiggle their way between nextTick
and setImmediate
:
I would not recommend relying on the exact execution order of non-chained events. If you want to control the execution order — rearrange the callbacks in a way so that the one that you want to be executed later depends on the one that you want to be executed earlier, or implement a queue (that does the same behind the hood).
In other words, if you depend upon a particular timing of asynchronous events, then you should actually chain them in your code so one must happen after the other via your code rather than relying on unspecified scheduling in the implementation.
Solution 2:
In HTML terms, the event loop for a page or set of pages from the same domain can have multiple task queues. Tasks from the same task source always go into the same queue, with the browser choosing which task queue to use next.
Tasks to run timer call backs come from the timer task source and go in the same queue. Let's call this queue task queue "A".
The ECMAscript 2015 (ES6) specification requires tasks to run Promise reaction callbacks to form their own job queue called "PromiseJobs". ECMAscript and HTML specifications do not use the same language, so let's notionally equate ECMA's "Promise Job queue" with HTML task queue "B" in the browser - at least a different queue to the one used by timers.
Theoretically a browser could choose tasks from either queue A or B to run, but in practice the promise task queue gets higher priority and will empty before a timer call back gets run.
This is why "h" gets logged last. Promise then
calls on fulfilled promises place jobs in the promise queue, which get executed with higher priority than timer call backs. The promise queue only becomes empty after console.log(3)
has been executed, which allows the timer call back to execute.
Advanced
ECMAScript guardians chose not to use HTML5 terminology or description of task queues in their specification because ECMAScript can run in more environments than just HTML browsers.
Native implementation of promise queues may use a "micro task" queue instead of a separate dedicated promise task queue. Micro queued jobs are simply run after the current script thread and any tasks previously added to the micro queue complete.
Detail of micro task queuing is not required to understand promises.
Promise polyfills for browsers which lack native support for promises (all versions of IE etc) may use timers and not behave in exactly the same way as native implementations when it comes to the order of promise reactions and timer call backs.
Solution 3:
I found this one easy to understand for someone new to JS.
This is copy paste from @getify's book
to use a metaphor: the event loop queue is like an amusement park ride, where once you finish the ride, you have to go to the back of the line to ride again. But the Job queue is like finishing the ride, but then cutting in line and getting right back on.
event loop queue - for all async callbacks other than promises, h
job queue - for all async callbacks related to promises. 1, 2, 3
Sync - a, b
Solution 4:
As of Es6, job queue runtime added to accommodate the promises. with new Promise()
we handle async code natively. setTimeout
is not part of javascript, it is part of the web api which is provided by the browser.
Now we have two queues. callback queue and job queue. Job queue is also called micro task queue.
The key thing is here, JOB QUEUE HAS HIGHER PRIORITY OVER CALLBACK QUEUE. so in your example, first synchronous code is executed.
console.log('a'); // a
console.log('b'); // b
then promises are sent to the job queue and setTimeout() is sent to the callback queue. Now event loop, checks the job queu first regardless how long is the setTimeout() is set for. since queue implements "first in first out" they are executed as they are ordered since they are just logging to the console.
promise.then(function(resolve) {console.log(1)}); // 1
promise.then(function(resolve) {console.log(2)}); // 2
promise.then(function(resolve) {console.log(3)}); // 3
after job queue is cleared out, event loop checks the callback queue
setTimeout(function() {console.log('h')}, 0); // h