0

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/

10
  • 2
    If you need X to do Y, and it takes time to get X, then it also takes time to do Y. Commented May 21, 2024 at 17:51
  • 1
    "I don’t understand why it’s closures all the way back to main though?" - Javascript (as used in a web-browser or NodeJS script) doesn't have a main function the same way that a C or Java program does, though. Commented May 21, 2024 at 17:52
  • I think you're misusing or misunderstanding the term "closure" here - provided the continuation doesn't reference any locals or parameters from outer lexical scopes then there's no requirement for the compiler to create a closure with capture semantics - the continuation can be interrnally represented by a static (in the Java/C# sense) function. Commented May 21, 2024 at 17:53
  • Could someone explain why async code needs to be called by async code That's not true. Async code creates results in async way. But can be called both from sync and async. Commented May 21, 2024 at 17:57
  • 1
    I think I was possibly taking this too literally “You can only call a red function from within another red function.” If I read a bit further it says this which seems more accurate: “If you call an async function you’ve got this wrapper object when you actually want the T. You can’t unwrap it unless you make your function async and await it.” Commented May 21, 2024 at 20:13

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

Comments

-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.

3 Comments

No, the distinction is not only (or mainly) syntactic.
@Bergi can you elaborate on what the distinction is for?
yeah what is it for @Bergi

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.