비동기, 동기

참고
콜백 헬은 코드가 활처럼 굽어 보기가 엉성해지고, 프로미스 헬도 지나친 then 핸들러 남용으로 인해 구현하고자 하는 의도를 한번에 파악할수가 없다.자바스크립트 async 와 await 는 이런 문제들을 해결하기 위해 탄생하였으며, 문법에 있어서도 훨씬 단순해져 가독성과 유지보수성을 향상 시켜준다.
function 키워드 앞에 async 만 붙여주면 되고, 비동기로 처리되는 부분 앞에 await 만 붙여주면 된다.
async 키워드는 어렵게 생각할 필요없이 await를 사용하기 위한 선언문 정도로 이해하면 된다. 즉, function 앞에 async을 붙여줌으로써, 함수내에서 await 키워드를 사용할 수 있게 된다. 이는 반대로 말하면 await 키워드를 사용하기 위해선 반드시 async function 정의가 되어 있어야 한다는 말과 같다.
async function에서 어떤 값을 리턴하든 무조건 프로미스 객체로 감싸져 반환 된다는 특징을 알 수 가 있다.
// async/await 방식의 예외처리도 똑같이 try, catch를 사용 async function func() { try { const res = await fetch(url); // 요청을 기다림 const data = await res.json(); // 응답을 JSON으로 파싱 // data 처리 console.log(data); } catch (err) { // 에러 처리 console.error(err); } } func();
JavaScript
복사
<함정과 병렬처리> 그러나 await를 깊은 이해 없이 막무가내로 사용하게 된다면 성능 문제 및 기타 문제가 발생할 수도 있다. await 자체가 Promise가 해결될 때까지 함수 실행을 일시 중지하는 것인데, 병렬적으로 멀티로 처리할 수 있는 작업을 억지로 동기적으로 처리하게 함으로써 오히려 2초만에 해결할 로직을 6초씩이나 걸리게 할 수 있기 때문이다.
async function getFruites(){ console.time(); let a = await getApple(); // 1초 소요 let b = await getBanana(); // 1초 소요 console.log(`${a} and ${b}`); console.timeEnd(); } getFruites(); //원래 각각 1초에 동시에 실행될 수 있는 함수인데, await을 통해 2초에 결과가 나옴. //이럴땐 어떻게하나요 ? async function getFruites(){ let getApplePromise = getApple(); // async함수를 미리 논블록킹으로 실행한다. let getBananaPromise = getBanana(); // async함수를 미리 논블록킹으로 실행한다. // 이렇게 하면 각각 백단에서 독립적으로 거의 동시에 실행되게 된다. console.log(getApplePromise) console.log(getBananaPromise) let a = await getApplePromise; // 위에서 받은 프로미스객체 결과 변수를 await을 통해 꺼낸다. let b = await getBananaPromise; // 위에서 받은 프로미스객체 결과 변수를 await을 통해 꺼낸다. console.log(`${a} and ${b}`); // 본래라면 1초+1초 를 기다려야 하는데, 위에서 1초기다리는 함수를 바로 연속으로 비동기로 불려왔기 때문에, 대충 1.01초만 기다리면 처리된다. }) //또 다른방법으로는 Promis.all()을 사용하라 async function getFruites(){ console.time(); // 구조 분해로 각 프로미스 리턴값들을 변수에 담는다. let [ a, b ] = await Promise.all([getApple(), getBanana()]); console.log(`${a} and ${b}`); console.timeEnd(); } getFruites();
JavaScript
복사
또다른 방법으론Promise.all() 정적 메서드를 사용하는 방법이 있다. 위와 같이 구성할 경우 비동기 처리 완료 시점을 가늠하기 힘들기 때문에 대부분의 실무에선Promise.all()로 처리한다.Promise.all() 은 배열 인자의 각 프로미스 비동기 함수들이 resolve가 모두 되야 결과를 리턴 받는다. 배열인자의 각 프로미스 함수들은 제각각 비동기 논블록킹으로 실행되어 시간을 단축 할 수 있다. 리턴값은 각 프로미스 함수의 반환값들이 배열로 담겨져 있다.
<top-level await>
Top-level await 란 async 함수나 모듈 외부에서도 await 키워드를 사용할 수 있게 해주는 편의 기능이다. 기존에는 await 키워드를 사용하기 위해선 무조건 async function를 정의하고 async 함수를 호출하는 식으로 사용해와야 했다. 그래서 굳이 함수로 감싸지 않아도 될 코드를 어거지로 함수로 감싸 IIFE로 호출하는 식으로 사용해왔다.
// Top Level에선 async funciton 정의없이 곧바로 await 키워드 사용이 가능하다 const res = await fetch(url); // 요청을 기다림 const data = await res.json(); // 응답을 JSON으로 파싱 console.log(data);
JavaScript
복사