• 카테고리

    질문 & 답변
  • 세부 분야

    풀스택

  • 해결 여부

    해결됨

await 질문드립니다

22.03.12 12:51 작성 조회수 136

0

강사님 안녕하세요

await 과 비동기 콜백함수에 대해 질문드리고 싶습니다

function delayP(sec){
    return new Promise(
        (resolve, reject) =>{
            setTimeout(()=>{
                resolve("Hello");
            }, sec);
        }
    );
}


async function myAsync(){
    await delayP(10000)  
    return 'async'
  }

myAsync().then((result)=>{
  console.log(result);
});

console.log("Hello");
console.log("Hello2");

이 소스코드를 봐주시면

await delayP(10000)

가 실행되기 전까지는

return 'async' 로 내려가지 못하도록

await이 기다려주는 기능을 알 수 있습니다

 

그러나 여기서 햇갈렸던 부분은

경과1.

 

 

경과2.



이렇게 됩니다

태스크 큐에 있는 함수는

호출스택에서 함수들이 전부 비워질 때까지 기다려야 하는데


경과3.

호출스택의 함수들도 다 같이 멈춘 다음에
await 과 관련된 호출 스택의 함수들은 전부 멈추고
전역 스코프의 console.log( ) 함수들은 실행되었습니다

 

 

경과4. 호출스택이 안비워졌는데 태스크큐에 있는 함수가 실행되었습니다

 

저는 태스크큐에 있는 함수들은 호출스택이 비워져야 실행이 된다고 생각해왔어서
await 이 이런 걸 가능하게 해주는 건지 질문드리고 싶습니다

답변 1

답변을 작성해보세요.

1

아뇨 await은 그냥 .then일 뿐입니다. 호출스택도 비워집니다.

setTimeout이 백그라운드로 넘어간 순간, 기존 함수들은 전부 종료되어 호출스택에서 빠집니다.

그러면 myAsync의 return 부분은 뭐냐? 하시겠는데

이건 코드를 promise로 바꿔서 생각하셔야 합니다. async 함수는 그냥 promise의 문법설탕일 뿐입니다.

function myAsnyc() {
  return delayP(10000).then(() => 'async');
}

입니다.

강사님 답변 남겨주셔서 감사합니다
말씀이 이해가 어려워서
직접 디버깅을 해봤습니다

경과1.
11행의 myAsync( ) 는 호출스택에 쌓여있는 상태로

경과2.
결국 호출스택은 비워지지 않은 상태에서
태스크큐의 resolve('Hello') 가 실행된 거라고 이해가 되는 것 같습니다

경과3.
그다음에서야 myAsync( ) 는 리턴하고 비워집니다


태스크큐의 함수들은 스택호출이 전부 비워져야 실행되는 거라고 생각되어서
이 부분이 이해가 어렵습니다

그리고 또 이해가 어려운 부분이 있습니다

아래 이미지를 봐주시면

12행에서 myAsync( ) 는 new Promise( ) 를 리턴하지 않고 문자열 'async'를 리턴했는데,

어떻게 19행에서 myAsync().then( ) 이렇게 then을 쓸 수 있는지도 궁금합니다

제가 지난 질문에서 말씀드렸습니다. 문자열이든 숫자든 리턴하면 Promise.resolve로 감싸진다고요. 그래서 then을 쓸 수 있다고요.

디버깅을 하셨다했는데 뭘 실험하신건지 이해가 안 됩니다. 콘솔 결과는 아무것도 보여주지않습니다.

경과 2에서 갑자기 어떻게 호출스택이 비워지지않았다는 결론이 나오신거죠?? 애초에 setTimeout 자체가 호출스택이 비워지지않으면 콜백함수가 실행이 안 되는데요.

강사님 주말에도 도와주셔서 감사합니다

제가 콘솔 결과로 보여지는 약간 다른 소스코드를 보여드리겠습니다

myAsync( ) 함수가

시작되면 호출스택에 쌓입니다

중간에

 setTimeout(()=>{
                resolve("10초 후, resolve()는 태스크큐로 갔다가 실행");
            }, sec);
      }

이 코드가 실행되고

10초 뒤에 태스크큐에서

resolve("10초 후, resolve()는 태스크큐로 갔다가 실행");

이게 실행이 됩니다

호출스택에 쌓여져있는

myAsync( ) 함수가 비워지지 않았는데

먼저 태스크큐의 resolve( )가 실행이 됩니다

아래의 소스코드입니다

function delayP(sec){

    return new Promise(

        (resolve, reject) =>{

            setTimeout(()=>{

                resolve("10초 후, resolve()는 태스크큐로 갔다가 실행");

            }, sec);

        }

    );

}

 

async function myAsync(){

    console.log("호출스택에 myAsync()가 쌓입니다")

    await delayP(10000).then((va)=>console.log(va));   

    console.log("다음 줄에서 리턴 후 호출스택에 myAsync()가 지워집니다")

    return 'async'

  }

 

myAsync().then((result)=>{

  console.log(result);});

 

console.log("전역 스코프의 함수 실행");

 

호출스택에 쌓여져있는

myAsync( ) 함수가 비워지지 않았는데

먼저 resolve( )가 실행이 됩니다

 

이 말씀이 이해가 안 됩니다. 왜 myAsnyc 함수가 비워지지 않았다고 생각하시는 거죠? 비워졌기에 resolve가 실행되는 겁니다. delayP(10000).then(...) 이 호출된 순간 myAsync는 끝나서 호출스택에서 나갑니다. 호출스택이 다 비워진 후에 setTimeout의 resolve가 실행되는 것이고요. 그 다음에 then에 등록해놨던 콜백이 다시 태스크큐에서 들어와 실행되겠죠.

제 생각에는 async function때문에 헷갈리시는 것 같은데 async function을 then을 쓴 프로미스 코드로 바꿔보세요.

myAsync( )가 호출스택에서 비워지지 않았습니다


그 증거로
myAsync( ) 함수의 12행 13행이 실행되고
13행에서 delaayP( )를 호출하고
태스크큐의 resolve( ) 가 실행되고 그 다음 차례에
myAsync( ) 함수 안의 14행부터 15행이 그 다음에 실행되었습니다

일부러 타이머도 10초나 주었습니다
그때까지 myAsync( )는 호출스택에서 안비워지고 종료되지 않고
태스크큐의 resolve( )가 실행될 때까지 기다려주었습니다

그 다음에서야
13행이 제대로 완수가 되고, 14행, 15행까지 진행된 다음 종료가 됩니다
이때  비로서 myAsync( ) 도 호출스택에서 비워집니다

호출스택에 쌓여진 myAsync( ) 는 끝나지 않았습니다
이 함수가 실행되는 도중에 태스크큐의 resolve( ) 가 호출된 것이고

myAsync( ) 는 await부터 15행 return 까지 끝난 후에 종료됩니다

당연히 제 의견이 틀리고, 강사님 말씀이 옳으니 질문드리지만

제 의견의 근거는 콘솔창의 출력입니다

myAsync( ) 는 await부터 15행 return 까지 끝난 후에 종료됩니다.

이게 틀렸습니다. 제가 async 함수는 다르게 봐야한다고 누누히 말씀드리는 이유입니다. 꼭 promise.then 문법으로 바꿔보세요. 함수가 다르게 보일 겁니다.

주말에도 쉬시지 못하시고 도와주셔서 감사드립니다
async를 promise 구문으로 바꿔주신 부분은
직접 실행시키고 
한줄 한줄 그림으로 그린 다음 다시 질문드리겠습니다
고생 많으셨습니다

myAsync 함수의 실제 body 부분은 await 전까지(await은 await보다 오른쪽 부분이 먼저 실행됩니다)입니다. 거기서 myAsync함수는 끝입니다. 참고해서 바꿔보세요.

아뇨.. await에 그런 기능은 없습니다. 그냥 promise 함수로 바꿔보세요. 바로 왜 호출스택이 비워질수밖에 없는지 아실 수 있습니다. 복원도 호출스택이 복원된다기보다는 실행컨텍스트의 스코프 체인이 남아있는겁니다.

앗 새로운 질문에 댓글로 다시 질문드리려고 지웠는데, 이미 읽으셨군요
다른 분들께서 어떤 질문이었는지 궁금해하실 것 같아서
다시 복원합니다


await이 호출스택을 전부 비워주는 기능이 있고
resolve( ) 가 실행될 때는 resolve( )가 실행될 때 필요한 호출스택을 복원해준다고 이해해도 되는지 드리는 질문이었습니다

그러면 혹시, await 이 호출스택을 비워주는 게 아니라
async로 선언된 함수의 body 부분의 끝이 await 전(오른쪽) 까지이므로

await의 전인 오른 쪽부분 delayP( ) 가 끝나니

호출스택에서도 자연스럽게 myAsync( ) 이 호출스택에서 비워진 거라는 말씀으로 이해해도 되나요?

그리고
태스크 큐의 resolve( )가 호출스택으로 올라가 실행될 때는 연쇄적인 스코프 체인이 남아있기에


resolve( )의 스코프와 연쇄된 스코프의 함수들이

호출스택에 다시 쌓이는 것이라는 말씀으로 이해해도 될까요?

네 맞습니다. 실제로 호출스택에 다시 쌓이는건지는 정확하게는 모르겠는데 그렇게 이해하셔도 문제는 없을것 같습니다.