JavaScript/DreamCoding

비동기 async와 await & Promise APIs

느리지만 꾸준하게 2021. 8. 3. 20:06

async와 await는 promise위에서 간편한 APIs를 제공하는데

기존에 존재하는거 위에 혹은 기존에 존재하는 것을 감싸서 간편하게 쓸 수 있는 API를 제공하는 것은 syntactic sugar라고 한다. 한가지 예로 class가 있었는데 JS에서 class는 전혀 새로운 것이 아니라 prototype을 베이스로 한 그 위에 살짝 덧붙여진 것이다. 

 

첫번째로 볼것은 async & await이다

promise를 유지해서 써야지 맞는 경우가 있고 async와 await로 변환해야지 좀 더 깔끔해지는 경우가 있다. 이러한 차이점은 프로젝트를 해보면서 경험해보자.

async를 사용법을 보면

예를 들어 사용자의 데이터를 벡앤드에서 받아오는 것이있으면 네트워크 통신을 해서 벡앤드 데이터를 받아오는데 10초정도 걸리는 코드가 있다고 가정하자. 10초가 지나면 받아온 사용자의 이름을 리턴하는 함수를 만들어본다고 하자.

 

호출을 하고 콘솔 찍으면 무언가 오래걸리는 코드를 비동기적인 처리를 전혀 하지 않으면 자바스크립트 엔진은 동기적으로 코드를 수행하기 때문에 한줄 한줄씩 코드를 실행하기 때문에 fetchUser가 호출됐다 하면

 

함수가 선언된 곳으로 가서 함수의 코드블럭{}을 실행하고 한줄이 수행되면서 10초동안 머무르고 있다가 10초가 지나서 성공적으로 네트워크 데이터를 받아오게 되면 그제서야 다음줄로 넘어가면서 jay가 리턴이 된다. 리턴된 코드가 user에 할당이 되고 다음 라인으로 넘어가서 jay가 출력이 된다.

 

여기서 비동기적인 처리를 전혀하지않으면 사용자의 데이터를 받아오는데 10초가 걸리기 때문에 만약에 웹페이지에서 UI를 표시하는 기능을 수행하는 코드들이 있다하면 이것이 끝나는 동안 데이터가 웹페이지에 표시가 안되기 때문에 사용자는 10초정도 텅 빈 웹페이지만 보게 된다.

 

이렇게 오래걸리는 일은 비동기적으로 처리할 수 있게 해야 하는데 저번에는 이것을 Promise로 만들었다. then이라는 콜백함수만 등록되어있으면 user의 데이터가 준비되는데로 사용자가 등록한 콜백함수를 불러준다라고 약속하는 것이다.

 

resolve와 reject를 호출하지 않고 리턴하게 되면 promise가 pending상태가 되는 것을 볼 수 있다. 그래서 promise안에는 resolve나 reject를 이용해서 완료를 해줘야 한다.

resolve와 reject를 붙이지 않았을 때
pending상태로 머무른다.

함수앞에 async라는 키워드를 붙여주면 번거롭게 promise를 쓰지않아도 자동적으로 함수안에 있는 코드블럭들이 promise로 변환이 되어진다. 출력하게 되면 똑같이 fetchUser가 프로미스를 리턴하게 된다. 즉 async 키워드를 함수앞에 쓰면 코드블럭이 자동으로 promise로 바뀌게 된다.

 

async와 await은 프로미스를 감싸고 있는 프로미스를 더 간편하게 쓸 수 있는 synthatic sugar라고 했는데 이렇게 키워드를 이용하면 프로미스로 바로 만들 수가 있다.

다음으로는 await이다.

await는 async라는 키워드가 부은 함수안에서만 쓸 수 있는데 delay라는 함수는 Promise를 리턴하는데 정해진 ms가 지나면 resolve를 호출하는 그런 promise를 호출하게 된다.

 

3초가 지나면 resolve를 호출하는 그러한 promise가 전달이 된다.

여기서 await이라는 키워드를 쓰면 delay가 끝날 때 까지 기다려준다. 그래서 2초 있다가 사과를 리턴하는 프로미스가 만들어진다.(async라는 키워드가 있으니까)

 

그리고 바나나는 똑같이 프로미스를 만드는 함수인데 이것도 1초있다가 바나나를 리턴한다. 

굳이 프로미스를 만들어보면 아래와 같다. 이렇게 chaining하는 거보다

위 그림과 같이 동기적인 코드를 작성하는거처럼 더 쉽게 이해할 수 있다.

이제 사과와 바나나를 한번에 다 따오는 pickFruits라는 함수를 만들자. 기존의 프로미스 chaining을 이용하게 되면 

return getApple()을 먼저 부른 다음에 사과가 받아와지면 받아온 사과를 이용해서 getBanana 바나나를 받아온다.

 

그리고 바나나가 되면은 그러면 리턴할 것인데 string template을 이용해서 사과와 바나나를 동시에 묶어주는 것을 리턴할 것이다. 최종적으로 부를 때는 pickFruits().then을 이용해 다 받아와지면 콘솔을 찍어본다. 3초정도 기다리면 사과와 바나나가 출력된다. 코드를 보면 콜백지옥이 떠오른다. 프로미스를 중첩적으로 chaining하게 되니까 콜백지옥이 보이는 것이다.

promise의 콜백지옥이 연상됨

async 키워드를 이용하면 간단하게 만들 수 있다. await를 이용해서 사과와 바나나 다 받아오고 그런 다음에 return하면

콘솔창에 같은 결과가 나온다. applePromise를 만들면 프로미스 안에 들어있는 코드블럭이 바로 실행된다.

await을 이용함

마지막으로 프로미스에서 제공하는 APIs를 보자

함수를 만들고 프로미스에 있는 all()이라는 APIs를 쓰면 된다. 프로미스 배열을 전달하게 되면 모든 프로미스들이 병렬적으로 다 받을 때까지 모아주는 것이다. 다 받아진 배열이 전달이 되고 배열을 string으로 묶는 join을 사용하고 출력하면 사과 + 바나나가 나온다.

다음으로 첫번쨰 받아지는 과일만 받아오고 싶다하면

프로미스의 race라는 API를 이용해서 배열에 전달된 프로미스 중에서 가장 먼저 값을 리턴하는 것만 전달이 되어진다.

결론적으로 async와 await은 프로미스를 간편한게 쓸 수 있는 것들이다. 그리고 프로미스에는 all이나 race같은 유용한 APIs가 있다.

'JavaScript > DreamCoding' 카테고리의 다른 글

primitive 타입과 object의 차이점  (0) 2021.08.03
JavaScript function 재정리  (0) 2021.08.03
프로미스 활용하기(JavaScript Promise)  (0) 2021.08.03
비동기 처리 Callback 이해하기  (0) 2021.08.03
JSON 활용방법  (0) 2021.08.03