skip to Main Content

Could someone explain why async code needs to be called by async code in a few more words than this? I understand that async-await is just promises under the hood, so it makes sense that any code after an await is equivalent to a callback in then(). I also have an intuition for why internally the code after an await would need to close over anything before it, if it’s broken up into a separate callback. I don’t understand why it’s closures all the way back to main though?

So with callbacks, promises, async-await, and generators, you ultimately end up taking your asynchronous function and smearing it out into a bunch of closures that live over in the heap.
Your function passes the outermost one into the runtime. When the event loop or IO operation is done, it invokes that function and you pick up where you left off. But that means everything above you also has to return. You still have to unwind the whole stack.
This is where the “red functions can only be called by red functions” rule comes from. You have to closurify the entire callstack all the way back to main() or the event handler.

https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/

2

Answers


  1. Why can async code only be called by async code?

    As written that’s not quite right: you can call it from anywhere you like. But if you care about the result — as is usually the case — then you’re going to need to wait for that other async code to finish, which will in turn make your code asynchronous. You can’t interact with a value that doesn’t exist yet.

    I don’t understand why it’s closures all the way back to main though?

    By this, the author means that async code is going to execute a bit at a time, with periods in between where no code is executed. So the code will run for a bit to set things up (eg, make a network request or start a timer), and then return. In the case of async/await code, it’s the await keyword that tells it to return.

    Eventually, code execution will resume. If you’re writing with callbacks or .then, the resuming pretty obviously happens in a separate function/closure, since you explicitly wrote a separate function. In the case of async/await that detail is hidden from you, but resuming still starts up a separate call stack, using variables that were preserved on the heap.

    Login or Signup to reply.
  2. You can call async code from non async code. You can only use await from within async functions.
    This is a syntactic restriction more than anything else. By marking a function as async, you clearly specify that it returns a promise. Which makes it clear to everything down the line that it should expect a promise.

    const getValue = ()=>{
      return 4;
    }// returns a number
    
    const getValueAsync = async ()=>{
       return 4;
    }//Returns a Promise<number>
    

    The async version of this is returning a Promise even though nothing inside is actually async.

    const retValueAsync = getValueAsync()// Promise<4>
    const retValue = getValue() // 4
    

    IF we don’t demarcate this. A function could return a promise or an actual value.

    const getRemoteConfigValue= ()=>{
      const response= await axios.get("http://configservice.com/config?name="+name)
      return response.data.value
    
    
    }
    
    const CONFIG = {
       COMPANY_NAME: "microsoft",
       COMPANY_ADDRESS: "123, Microsoft St, Microsoft City"
    }
    
    function getConfigValue(name){
    
       if(CONFIG[name])
          return CONFIG[name];
       return await getRemoteConfigValue(name);
    
     
    }
    

    This can cause an issue because its easy to expect a value and end up with a promise. It can be hard to spot because of the await.
    Yes, you can just skip the await but it being there warns you that the whole function returns a promise not just a value

    TLDR: Its syntactic. No real reason, but backward compatibility and convention.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search