async /await 에 대하여
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
https://nayoungkim00.tistory.com/43
📚 자바스크립트의 비동기 처리 동작 원리 | 콜 스택, 이벤트루프, 콜백헬, Promise, fetch vs axios, asy
목차 1. 비동기처리의 필요성 1-1. 실행컨텍스트 스택 1-2. 싱글스레드 2. 비동기처리의 동작 원리 2-1. 브라우저의 이벤트 루프 2-2. 왜 싱글스레드일까? 3. 콜백 비동기 처리의 문제점 3-1. 콜백 헬 (Ca
nayoungkim00.tistory.com