javascriptasync-awaitpromisecallstackexecutioncontext

Does the both functions parent and asynchronous child get re-pushed to call stack from task queue on completion of async code of child function?


What i all know about call stack is:

  1. If there is only one function being called it will be pushed to call stack and removed when its job is done or otherwise return statement is encountered.

  2. If one function calls another one, both stays in call stack and popped out in a order; children then parent.

  3. If there is asynchronous code in child function, both are popped out as said in statement 2. but when the asynchronous job is done in future, then child (?) function will be put into Task Queue and then into Call Stack.

But when i used debugger in VSCode:

  1. I found that both parent and child functions are in call stack after completion of setTimeout(). making my belief false of only child being in call stack.
  2. In below code after resolve of promise only const dummy2 = 'abc' is available in debugger after completion of asynchronous code, and not the const dummy = 'XYZ'

function asyncfun(){
  const innerDummy = '123'
 return new Promise((resolve, reject)=>{
      setTimeout(()=>{
        resolve('You are eligible for voting.');
      },5000)
 })
}

function outer(){
  const dummy = 'XYZ';
  asyncfun().then((msg)=>{
    const dummy2 = 'abc'
    console.log('Promise result:',msg)
  })
}
outer();

  1. Note that i had put breakpoints on line: resolve('You are eligible for voting.'); inside setTimeOut() callback, to see who is in call stack after completion/resolve of promise, which happens in 5 seconds in future. Meanwhile when execution reaches to this line, innerDummy is not available to use.

  2. Another breakpoints on line console.log('Promise result:',msg) where execution reaches after the above one. Here dummy is not available to use but dummy2 is.

  3. Because of above variables not available for in debugger i am confused whether partial functions are re-pushed to call stack or what?


Solution

  • I think you might be confusing the concepts of call stack and scoping here a little bit.

    I found that both parent and child functions are in call stack after completion of setTimeout(). making my belief false of only child being in call stack.

    The completion of setTimeout here is instant, since you're just returning a promise that will be pushed on to the task queue. At that point your callstack should contain outer => asyncfunc.

    You return the promise from asyncfunc and outer is done as far as the synchronous part of the execution is concerned. Then after the timeout passes, internally the JS engine pushes the timeout callback on to the task queue. Conceptually your task queue at that point only contains that anonymous callback of setTimeout, but some debuggers show async differently and sometimes you can see the trace of where the callback was handled (this will be different for different JS implementations like Node, Deno or Chrome, since callback handling isn't really a part of the JS language, which only by itself is synchronous).

    In below code after resolve of promise only const dummy2 = 'abc' is available in debugger after completion of asynchronous code, and not the const dummy = 'XYZ'

    I'm assuming your breakpoint here is somewhere around console.log('Promise result:', msg). If that's the case, both your dummy and dummy2 variables will be available (you can check that by console logging them after the dummy2 declaration), but dummy is not part of your local scope, so you should see it under Closure in the debug panel.

    So to answer your main question - no, only the async callback gets pushed to the call stack by the task queue. Basically only this

    () => {
      resolve('You are eligible for voting.');
    },
    

    ends in your callstack after the timeout is done. If you're asking about what happens with the function after .then(..), that's handled a bit differently in a so-called "Microtask queue". It might be helpful dropping your code in here https://www.jsv9000.app/ to visually check how your execution looks.

    Hope that helps :)