javascript

[javascript] async, await

미 성 2024. 3. 15. 03:22

 
안녕하세요 ! 오늘은 지난 시간에 배웠던 Promise (프로미스) 를 더 간지나게 사용할 수 있게 해주는 아이들인 async와 await에 대해 배워보겠습니다. 🥦



[javascript] 비동기 처리(callback, Promise) - https://with-mimi.tistory.com/m/entry/javascript-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%ACcallback-Promise

[javascript] 비동기 처리(callback, Promise)

안녕하세요! 🩶 오늘은 1. 비동기 처리 2. callback 함수 3. Promise 객체 에 대해 하나하나 알아보겠습니다. 자바스크립트를 해오신 분들이시라면 아시겠지만, 자바스크립트에서는 코드를 위에서부

with-mimi.tistory.com



 
지난 포스팅에서 아래와 같이 프로미스 객체를 반환하는 함수를 만들어서 다양한 프로미스 객체를 만드는 데에 활용했던 거 기억하시나요? 복습하는 차원에서 다시 한번 눈으로 따라가 보세요 ! 👀

 


그런데 위처럼 함수의 return값을 굳이 프로미스로 정의하지 않더라도 무.조.건. 🚨함수의 그 어떤 리턴값도 모두 프로미스의 형태로 반환할 수 있게 해 주는 문법이 🚨있답니다. 바로 "async" 인데요, ✔️함수 선언 맨 앞에 적어주시면 됩니다. 



                                             🤔

 
function 앞에 async를 붙이면 해당 함수는 항상 Promise를 반환합니다. 설령 Promise가 아닌 값을 반환하는 것처럼 보이더라도 이행 상태의 프로미스(resolved promise)로 값을 감싸 이행된 프로미스( state:fulfilled, value:return값)가 반환되고 있습니다. 아래 코드에서 plus함수의 return값도 그냥 방정식 a+b 같아 보이지만 프로미스를 반환하고 있답니다.
 

 
 
함수 선언 맨 앞에 "async"를 붙인 plus()함수를 만들고, 외부에서 plus함수에다 then을 호출해보았는데, 자연스럽게 then메소드가 잘 굴러갑니다. plus함수가 리턴값을 프로미스로 주는 것이 사실이라는 의미가 되겠죠! 💡 결괏값으로는 4가 출력되겠습니다. async가 붙은 함수는 상태는 fulfilled이며 반환값을 value로 가지는 프로미스를 반환합니다. 그러므로 현재 함수에서의 리턴 프로미스Promise {(resolve,reject)=>{resolve(4)};  즉 Promise { <fulfilled> : 4 }일 것입니다. 앞선 시간에 배웠듯 이 value는 then함수에 인자로 전달되므로 4가 출력되게 되는 것입니다.
 

  ** 조금 헷갈리신다면 위 코드와 같이 async 함수가 Promise를 반환하고 있음을 명시적으로 드러내도 좋습니다 !







 
async 함수에서 에러를 처리하고 싶을 때throw new Error("...") 를 사용해주시면 됩니다. 그러면 async 함수는 이 구문을 통해 반환하려던 프로미스가 거부됩니다. 그리고 해당 에러를 통해 거부된 프로미스가 반환되게 됩니다. 이 에러가 난 프로미스는 외부에서 try...catch문을 통해 적절히 처리해주시면 됩니다. ( try… catch문에 대해선 포스팅 말단에 다루겠습니다. )






 



                                          📚🌱
 
지금부터는 async와 함께 동작하는 두 번째 새로운 문법"await"에 대해 알아보겠습니다! 👏🏻우선 이 키워드는 무조건 async함수 내에서만 사용 가능합니다. javascript는 await 키워드를 만나면 프로미스가 처리될 때까지 기다리게 됩니다. 다 기다린 후 결괏값을 반환합니다. 코드를 통해 살펴보겠습니다.
 

 

waiting이라는 함수를 만들어 보았습니다. 그리고 외부에서는 waiting함수를 호출하고 있습니다. waiting 함수 안을 보시면 일단 promise 변수에 새로운 프로미스가 할당되는 모습이 보이고, 그 다음 줄에는 let clock = await promise; 라는 코드가 보이는데요, 이 코드에서 await가 하는 역할은 바로 promise라는 이름을 가진 프로미스의 비동기적 실행이 끝날 때까지 기다려주는 것입니다.🫸🏻 😌 여기서 promise 내의 비동기적 실행이란, 2초가 지난 후 state를 이행됨(fulfilled)로 바꾸고 value 는 "2초 지났음"으로 바꿔주는 것이겠죠. 2초가 지나고, promise라는 프로미스 객체 내의 모습(property 2개)이 재정립된 후 이 바뀐 프로미스의 value값이 clock이라는 변수에 담기게 되는 것입니다. 최종적으로 clock을 출력하게 되면 당연히 clock 프로미스의 value값 "2초 지났음"이 출력되겠죠! ( await 키워드는 해당 프로미스가 이행될 때까지 대기하고 결과를 반환합니다. )







 
 
await의 역할 조금 감이 오시나요? 🙂 만약 위 코드에서 "await"키워드만 빼고 다시 코드를 돌려보면 출력값은
 
Promise { <pending> } 이 나온답니다. promise의 비동기적 실행을 기다려주지 않은 채 바로 출력으로 넘겨버리므로 그저 대기상태(pending)에 있던 프로미스의 모습 그대로가 출력되는 것입니다. 🤷🏻‍♀️
 







 
 
🌟 생각해보면 앞서 배웠던 then메소드와 catch 메소드도 프로미스의 비동기적 실행을 기다려준 후 핸들러를 실행했죠. 비동기적 실행과 그 후처리를 따로 분리시킴으로서 원하는 때에 해당 실행을 진행시키고 싶을 때에는 이전처럼 then과 catch메소드를 이용하시면 됩니다. 🌟그러나 await 키워드는 async 함수 안에서 쭉 같은 흐름으로 (동기적으로) 코딩할 수가 있죠. 👏🏻
 
 

 
예시 코드를 보면서 다시 정리해봅시다. 외부에서 우선 showname함수를 호출합니다. result 변수에는 "mimi" value가 1초 후에 할당되므로, 결론적으로는 "시작"이 출력된지 1초만에 "mimi"가 출력되겠네요.
 
 


오늘의 포스팅 맨 앞에 보여드렸던 코드를 async와 await, 그리고 try-catch 구문을 사용하여 다시 작성한 코드입니다! 우선 total 함수의 첫 번째 줄 await waiting(3000); 을 보시면 왜 await가 유용한 문법인지 감이 오실 겁니다. (인수만 바꿔주면 total함수를 자유자재로 지연시간 설정 가능)
또 주의해야 할 점은, 앞서 말씀드린 waiting같은 비동기 함수를 await가 호출한 경우라면 await가 유효하나, 비동기함수가 아닌 info와 같은 함수를 호출하는 경우에는 동기적으로 실행된다는 점입니다. value를 사용하여 최종적으로 console.log(value)을 통해 문장이 출력됩니다.




 
 
try... catch문에 대해서 따로 포스팅한 적이 없어 조금 첨언하자면, 우선 이 구문은 "예외발생 상황 지정"을 위해 존재하는 구문입니다. 여기서 "예외"란, javascript에서 따로 에러로 감지하지는 않으나, 개인적으로 나는 에러로 감지하기를 바라는 지점이 존재할 때를 의미합니다. 이 때 try...catch 구문을 사용하면 에러지점을 직접 지정하고 핸들링 할 수 있습니다. 🏎️💨
 

 
일반적으로는 try 속 내용에 아무런 문제가 없을 때는 catch문이 실행되지 않습니다. 그러나 위 코드는 현재 *모두화이팅* 이라는 글자가 중간에 들어가 불완전한 형태의 코드가 try문 안에 들어가 있죠. 이 때 "안녕하세용" 창까지는 잘 출력되다가 모두화이팅 부분에 코드가 도달했을 때 오류를 감지하고 바로 catch문으로 넘어가 catch문 내용을 수행하게 됩니다. catch는 에러를 잡아서(catch) 무언가를 더 할 수 있도록 해주는 역할입니다. 🫴🏻🚨 따라서 위 코드를 실행시키면

🖥️
"안녕하세용"
"오류가 감지되었습니다."
🖥️

이렇게 두 창이 연속적으로 뜨게 됩니다. 




 
 
원할 때 catch문을 실행하게 하는 것도 가능합니다 ! try문에서 throw 문을 실행시키면 되는데요, 이 때 javascript에 내장된 객체인 "Error"를 함께 사용합니다. throw문은 항상 원하는 걸 catch에게 던져줍니다.

 
현재 try문에서 문법적으로 잘못된 부분은 따로 없지만 에러 객체를 생성해서 throw로 던져주는 코드가 존재하네요. 따라서 catch문도 실행되므로 위 코드를 실행시키면

🖥️
"저는 멍청합니다."
"error : 라고할뻔~"
🖥️
이렇게 두 창이 연속적으로 뜨게 됩니다.

** try… catch문은 동기적 실행에서만 사용이 가능하다.
( async 함수 내부는 동기적으로 진행되므로 async 함수 내부에서 try…catch문이 자주 쓰임. )




FINAL STUDY
 
 

 
지난 콜백 / 프로미스 글에서 여러 번 다루었던 코드를 이번에는 async/await 형식으로 바꾸어 다시 작성해 보았습니다. then 프로미스 체이닝보다 가독성이 높아진 것이 느껴지시나요?
 

 
바로 윗 코드에서 order 함수만 따로 다시 적어보았는데요, 이번에는 비동기 함수 f1 f2 f3을 병렬적으로 실행하기 위해 Promise.all을 써 보았습니다. 이로써 시작한지 3초만에 세 함수의 결괏값이 모두 담긴 배열이 출력되게 됩니다. ( 아 이 때는 세 함수 모두 resolve 상태의 프로미스를 반환해야 합니다 ! )






오늘은 async, await 문법을 알아보았습니다.
이제 한 주제만 더 다루면 드디어 길고 길었던 제 자바 스크립트 진도도 다 끝이 나게 되네요 . . !🥹
끝까지 같이 힘내봅시다 🔥 오늘도 좋은 하루 되세요. 🍀