-
async /await 에 대하여네트워크/CS Study 2023. 6. 14. 23:14
1. 사용하는 이유
비동기 처리를 하는 가장 기본적인 call back 함수를 이용한 처리와 promise 객체를 이용한 처리가 단점이 많다.
promise 객체를 더욱 더 가시성 좋게 사용하기 위해 사용합니다
promise와 callback의 단점
1. 보기에 너무 나쁘다 call back 지옥 보면 토나오는 모양새이고 promise 또한 then으로 병렬적으로 쓰여진 것도 에러가 발생했을 때 찾기 좀 힘들고 들여쓰기가 많이 들어간다면 정신건강에도 나쁘다.
2. catch() 메서드를 사용해서 에러에 대해서 예외처리를 할 때 동기코드와 비동기코드가 섞여 있으면 누락이 있거나 난해해지는 경우가 있다.
그래서 async/await은 promise를 이용한 비동기처리의 불편함을 개선하기 위해서 ES7에서 새롭게 추가된 내용이다.
2. 문법 형태
var addCoffee = function (name) { return new Promise(function (resolve) { setTimeout(function(){ resolve(name); }, 500); }); }; var coffeeMaker = async function () { var coffeeList = ''; var _addCoffee = async function (name) { coffeeList += (coffeeList ? ', ' : '') + await addCoffee(name); }; await _addCoffee('에스프레소'); console.log(coffeeList); await _addCoffee('아메리카노'); }; coffeeMaker();
async 는 promise객체를 반환한다 만약 return 으로 promise 가 아닌 것을 반환을 한다해도 promise객체로 감싸서 반환을 시키기 때문에 then을 사용할 수 있다. await은 async문법 안에서만 작동을 하므로 그냥 사용할 수 없다.
최상위 코드( 걍 맨 위) 에서는 작동하지 않는다. 작동하려면 ()로 살포시 감싸줘야한다.
대충 promise 객체의 모양 이런갑다 생각을 하고 있자 promiseResult 안에 값이 들어간다는 것만 기억하면 됨
아래의 결과 값은 같다 이걸 보고서 promise를 async /await 형태로 변환하는 것의 느낌을 잡아봐야겠다. 느낌을 잡았다면 예시에 나온 것도 한번 봐보자.
async function foo() { await 1 }
function foo() { return Promise.resolve(1).then(() => undefined) }
promise.all 을 통한 병렬처리가 가능하다 이건 밑에 링크 들어가서 자세히 보셈
3.예시에 있는 예제를 통해서 더욱 개념을 단단히 잡아보자
async function myFunction() { try { const [result1, result2] = await Promise.all([asyncOperation1(), asyncOperation2()]); console.log(result1, result2); } catch (error) { console.error(error); } }
2. 동작원리
비동기처리의 동작원리를 이해하려면 콜스택과 테스트큐를 라는 공간을 이해를 하면 쉽다.
아래에 있는 로직의 작동 순서가 이해가 간다면 그냥 이 글 읽을 필요도 없음. (난 좀 충격적이였음)
const a = () => { console.log("a 시작"); b(); console.log("a 끝"); }; const b = async () => { console.log("b 시작"); await c(); console.log("b 끝"); }; const c = async () => { console.log("c 시작"); await d(); console.log("c 끝"); }; const d = () => { console.log("d") }; a();
결과만 말하자면 순서는 a시작 > b시작 > c시작 > d시작 > a끝 > c끝 > b끝 이며, 콜스택에서 실행되던 와중에 await을 만나게 되면 그 함수는 일시정지하며 테스크큐로 이동하게 된다 그렇게 되면 일시정지 하지 않고 있는 a끝이 먼저 실행되고 콜스택 순서대로 일시정지했던 함수들이 실행되며 빠져나가게 된다 이 과정은 콜스택이 빈 순간 이벤트 루프가 테스크큐에서 함수를 다시 콜스택으로 이동시키기 때문에 일어나는 것임을 기억하고 콜스택, 이벤트루프, 테스크큐 를 통한 작동원리를 알아보았
3. 예제
예제문제는 mdn에서 가져 온 내용이다 병렬처리과정에 대한 내용도 들어가 있고 promise와 await의 차이를 잘 볼 수 있어서 이 로직을 잘 이해한다면 거의 다 이해한거라고 봐도 될 듯하다
var resolveAfter2Seconds = function() { console.log("starting slow promise"); return new Promise(resolve => { setTimeout(function() { resolve(20); console.log("slow promise is done"); }, 2000); }); }; var resolveAfter1Second = function() { console.log("starting fast promise"); return new Promise(resolve => { setTimeout(function() { resolve(10); console.log("fast promise is done"); }, 1000); }); }; var sequentialStart = async function() { console.log('==SEQUENTIAL START=='); // If the value of the expression following the await operator is not a Promise, it's converted to a resolved Promise. const slow = await resolveAfter2Seconds(); console.log(slow); const fast = await resolveAfter1Second(); console.log(fast); } var concurrentStart = async function() { console.log('==CONCURRENT START with await=='); const slow = resolveAfter2Seconds(); // starts timer immediately const fast = resolveAfter1Second(); console.log(await slow); console.log(await fast); // waits for slow to finish, even though fast is already done! } var stillConcurrent = function() { console.log('==CONCURRENT START with Promise.all=='); Promise.all([resolveAfter2Seconds(), resolveAfter1Second()]).then((messages) => { console.log(messages[0]); // slow console.log(messages[1]); // fast }); } var parallel = function() { console.log('==PARALLEL with Promise.then=='); resolveAfter2Seconds().then((message)=>console.log(message)); resolveAfter1Second().then((message)=>console.log(message)); } sequentialStart(); // after 2 seconds, logs "slow", then after 1 more second, "fast" // wait above to finish setTimeout(concurrentStart, 4000); // after 2 seconds, logs "slow" and then "fast" // wait again setTimeout(stillConcurrent, 7000); // same as concurrentStart // wait again setTimeout(parallel, 10000); // trully parallel: after 1 second, logs "fast", then after 1 more second, "slow"
중요한 것은 await을 선언한 시점과 promise는 같은 결과를 어떻게 출력하는지에 대해서 잘 보면 된
4. 단점
에러핸들링에 try catch 구문이 필요하다 (근데 promise 에러핸들링하는거보다 보기 쉬운거 같다 개인적으로)
함수의 결과 값이 promise로 반환된다는 것이 명시적으로 나타나있지 않아서 헷갈리면 안된다.
5. 주변용어
스레드 : 커피숍 생각하기(처리할 수 있는 카운터 갯수) js는 싱글스레드
동기 / 비동기연산 : 커피숍 생각하기 (주문받고 음식나올 때까지 전담케어하면 동기, 진동벨 주고 빠른거 먼저 처리하면 비동기)
promise : https://spicycookie.me/JavaScript/promise/
promise 관련되서 궁금할 때에는 이거 보면 되겠다.
제너레이터 (function*) : 함수인데 중간에 원하는 부분에서 멈췄다가 그 부분부터 다 실행하는 함수
https://seo-tory.tistory.com/77
yeild를 비롯해서 여러가지 개념이 많이 나오네....
thenable 객체 : then 메소드를 갖는 객체를 뜻한다. 체이닝이나 await과 같은 promise 패턴을 가진 구문에서 사용할 수 있다. 모든 promise객체는 thenable 객체이지만 역은 성립하지 않음.
promise.then : 두개의 콜백함수를 매개변수로 받는데 각각 resolve 와 reject로 생각하면 된다.
p.then(onFulfilled, onRejected); p.then(function(value) { // 이행 }, function(reason) { // 거부 });
스레드 (≒task)
어떤 프로그램 내에서(프로세스) 실행되는 흐름의 단위 일반적으로 한프로그램에 한개 스레드fetch 함수 : fetch함수는 HTTP 요청 전송 기능을 제공하는 클라이언트 사이드 Web API
latency : 지연시간
참고자료:
https://www.daleseo.com/js-async-async-await/
https://sangminem.tistory.com/284
'네트워크 > CS Study' 카테고리의 다른 글
[항해99 WIL] ES5 VS ES6 (0) 2023.06.21 [CS Study] Closure (0) 2023.06.21 [CS Study] MVVM && FLUX (0) 2023.06.20 [CS Study] 스코프란 & 호이스팅이란 무엇일까??? (1) 2023.06.19 [CS Study] fetch / axios && API란 무엇일까? (1) 2023.06.18