最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Javascript event queue execution order.. Please give the output of the snippet with reasoning - Stack Overflow

programmeradmin2浏览0评论

Please predict the output along with the event queue reasoning of the below snippet.

I ran the snippet and I know the output but I would like to understand the event queue working and what gets higher preference and thus which logs first.

Here is the snippet:

console.log("Start");

setTimeout(() => console.log("Timeout 1"), 0);

async function asyncFunc() {
  console.log("Async Start");
  await new Promise((resolve) => setTimeout(() => resolve(), 0));
  console.log("Async End");
}

asyncFunc();
Promise.resolve().then(() => console.log("Promise 1"));
setTimeout(() => console.log("Timeout 2"), 0);
console.log("End");

Please predict the output along with the event queue reasoning of the below snippet.

I ran the snippet and I know the output but I would like to understand the event queue working and what gets higher preference and thus which logs first.

Here is the snippet:

console.log("Start");

setTimeout(() => console.log("Timeout 1"), 0);

async function asyncFunc() {
  console.log("Async Start");
  await new Promise((resolve) => setTimeout(() => resolve(), 0));
  console.log("Async End");
}

asyncFunc();
Promise.resolve().then(() => console.log("Promise 1"));
setTimeout(() => console.log("Timeout 2"), 0);
console.log("End");

My prediction for the above snippet:

("start","Async Start","End","Async End","Promise 1","Timeout 1","Timeout 2") 

as promises queue up at microtask, timeouts queue up at callback queue, general console.log() are executed synchronously unless they are awaited by a previous statement which moves them to the microtask queue ..(My understanding)

However this logic totally fails to explain why the output is

("Start" ,"Async Start", "End" , "Promise 1" , "Timeout 1" , "Async End" ,"Timeout 2") 

(actual output got by running in the editor)

Where am I wrong in my understanding?

Share Improve this question edited Mar 31 at 10:19 0stone0 44.4k5 gold badges52 silver badges80 bronze badges asked Mar 31 at 9:24 Racerr9Racerr9 195 bronze badges New contributor Racerr9 is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct. 9
  • 3 Rather than posting a new question, you should edit your original question so it's on-topic here on StackOverflow. Please see the tour and How to Ask – DarkBee Commented Mar 31 at 9:31
  • Actually my previous question closed by itself as it showed an error " not focused on what questions wants.." .I don't know who closed it. Also i have a new code snippet.. that entirely different than the previous three snippets though the underlying question is the same "event queue prioritization (microtask queue , callback queue, async await , Promise.then()... etc...).. – Racerr9 Commented Mar 31 at 9:37
  • Nobody got offended, questions get closed not because they're "bad" but because they're not suitable for the Q&A format of this site. But you still haven't responded to the comment asking why you care. If you just want to understand the event loop better, you should study some general resourced and then ask a specific question. What is your prediction of what the code will output (and why)? Is there something unexpected happening when you run it? – Bergi Commented Mar 31 at 9:47
  • 1 @Racerr9 I would suggest you add another console.log in the new Promise callback and one in the setTimeout callback before the resolve() call. That might clear it up what's happening with the 3 timeouts. – Bergi Commented Mar 31 at 9:59
  • 1 I have edited the question as per your format....I'm new to this forum ...@Bergi – Racerr9 Commented Mar 31 at 10:09
 |  Show 4 more comments

3 Answers 3

Reset to default 1

My prediction is promises queue up at microtask, timeouts queue up at callback queue

Not quite. It's promise handlers (the callbacks you pass to .then()) and callback functions (that were passed to setTimeout) that are getting queued. (Actually, it's a "task" or "job" object, which stores which function to execute, with which arguments, and what to do with the result).

And further, these tasks do not get queued immediately when they are defined. They are getting queued when the thing they were waiting for happens - the promise fulfills or rejects, or the timeout is reached.

In particular, the new Promise you were awaiting, fulfills only when the second timeout occurs, and that (where resolve() is called) is when the promise handler is scheduled on the microtask queue.

general console.log() are executed synchronously unless they are awaited by a previous statement which moves them to the microtask queue

Yeah, and it's not just console.log() statements but any code after an await. Really what's happening internally is that the remainder of the async function is put in a "resumption function" that is put on the awaited promise as a .then() handler. Same as if you had written

function asyncFunc() {
  console.log("Async Start");
  return new Promise((resolve) => {
    setTimeout(() => resolve(), 0);
  }).then(() => {
    console.log("Async End");
  });
}

So again, the () => { console.log("Async End"); } isn't scheduled to be executed until the promise fulfills.

Where am I wrong in my understanding?

You seem to have assumed that all tasks are immediately put into a queue. In your previous question you used only immediately-fulfilled promises (Promise.resolve()), here you're still using setTimeout that does not really wait (0ms) - but such things are the exception rather than the norm. We write asynchronous code because it has to wait for something happening (outside of the JavaScript execution) much later.

I would suggest to run the following code to understand better what's going on:

console.log("Start");
setTimeout(() => console.log("Timeout 1 (after 100ms)"), 100);
console.log("Timeout 1 created");

async function asyncFunc() {
  console.log("Async Start");
  const promise = new Promise((resolve) => {
    console.log("In `new Promise` executor");
    setTimeout(() => {
      console.log("Timeout 2 (after 300ms)");
      resolve();
    }, 300);
    console.log("Timeout 2 created");
  });
  console.log("inner promise created");
  await promise;
  console.log("Async End (after timeout 2)");
}

const p1 = asyncFunc();
console.log("p1 created");

const p2 = Promise.resolve().then(() => console.log("Promise 1"));
console.log("p2 created");

setTimeout(() => console.log("Timeout 3 (after 200ms)"), 200);
console.log("Timeout 3 created");
console.log("End");

It could help to list all actions in the order they are executed, together with the effect they have on the queues used by promise reactions and timeouts.

First, it will be easier to name all callback functions and promises involved. So here is the same script with assignment to variables that can be referenced in the above-mentioned list:

const t1 = () => console.log("Timeout 1");
const t2 = () => console.log("Timeout 2");
const p1_then = () => console.log("Promise 1");
const executor = (resolve) => setTimeout(resolve, 0);

console.log("Start");
setTimeout(t1, 0);

async function asyncFunc() {
  console.log("Async Start");
  const p0 = new Promise(executor);
  await p0;
  console.log("Async End");
}

const p3 = asyncFunc();
const p1 = Promise.resolve();
const p2 = p1.then(p1_then);
setTimeout(t2, 0);
console.log("End");

And now to the list:

The first column represents the callstack with as last entry the function that is currently executing an action.

For each of the involved promise objects there is a column listing its state (P=pending, F=fulfilled).

Finally, there are two columns that represent the current state of a queue: first the high-priority Promise Job Queue, and then the lower-priority task queue where timer jobs get queued.

Call stack Action p0 p1 p2 p3 Promise Job Queue Task queue
Script console.log("Start")
Script setTimeout(t1, 0) t1
Script asyncFunc() t1
Script > asyncFunc console.log("Async Start") t1
Script > asyncFunc new Promise(executor) t1
Script > asyncFunc > executor setTimeout(resolve, 0) t1, resolve
Script > asyncFunc p0 = .... P t1, resolve
Script > asyncFunc await p0 P t1, resolve
Script p3 = .... P P t1, resolve
Script p1 = Promise.resolve() P F P t1, resolve
Script p2 = p1.then(p1_then) P F P P p1_then t1, resolve
Script setTimeout(t2, 0) P F P P p1_then t1, resolve, t2
Script console.log("End") P F P P p1_then t1, resolve, t2
(empty) check promise job queue P F P P p1_then t1, resolve, t2
p1_then console.log("Promise 1") P F P P t1, resolve, t2
p1_then return (fulfill p2) P F F P t1, resolve, t2
(empty) check promise job queue P F F P t1, resolve, t2
(empty) check other queues P F F P t1, resolve, t2
t1 console.log("Timeout 1") P F F P resolve, t2
(empty) check promise job queue P F F P resolve, t2
(empty) check other queues P F F P resolve, t2
resolve fulfill p0 F F F P resume asyncFunc t2
(empty) check promise job queue F F F P resume asyncFunc t2
asyncFunc (resumed) console.log("Async End") F F F P t2
asyncFunc return (fulfill p3) F F F F t2
(empty) check promise job queue F F F F t2
(empty) check other queues F F F F t2
t2 console.log("Timeout 2") F F F F
(empty) check promise job queue F F F F
(empty) check other queues F F F F

Hope this helps.

Output would be - Start Async Start End Promise 1 Timeout 1 Async End Timeout 2

Explanation -

Firstly Synchronous Calls would be executed, therefore it would print start.

Then the synchronous call inside the Async function would be printed i.e. Async Start.

And further it would print another synchronous call i.e. End.

Now, Microtask runs before macrotasks, so it would print Promise 1.

Its the turn for Macrotask (setTimeout) runs and prints Timeout 1.

Now AsyncEnd is printed as its the turn of the async function to completely executed.

At the end another macrotask runs and prints Timeout 2.

发布评论

评论列表(0)

  1. 暂无评论