2019. 06. 08 수정
1. async / await
JS 프로그래밍을 하다보면 비동기 방식을 많이 사용하게 되는데요, 비동기 호출 후 이를 처리하는 콜백 함수의 개념은 매우 중요합니다.
즉, 비동기 프로그래밍에서 콜백 함수는 반드시 사용해야 하는 부분이죠.
그런데 콜백 함수가 깊어지면 코드가 복잡해지게 되므로( 가독성이 안좋아지므로 ) ES6에서 Promise를 도입했습니다.
( 콜백함수에 대한 내용은 여기를 , Promise에 대한 내용은 여기를 참고해주세요. )
하지만 Promise를 사용해도 여전히 코드가 복잡했는데요.
그래서 ES8에서 async와 await을 도입했고, 덕분에 비동기 코드를 동기적으로 깔끔하게 처리할 수 있게 되었습니다.
그렇다고 항상 async/await이 옳다고는 할 수 없습니다.
- 콜백의 깊이가 깊지 않을 때는 작성하기 간편한 콜백함수를 호출하거나, Promise를 사용하는 것이 더 나은 방법일 수도 있습니다.
- async/await은 Promise를 사용하기 때문에 Promise를 알아야 하고, async/await이 할 수 없는 동작을 Promise로 해결할 수 있는 경우도 있습니다.
이제 비동기 코드를 깔끔하게 작성할 수 있도록 도와주는 async/await에 대해 알아보도록 하겠습니다.
2. 예시로 보는 기본문법
1) async / await 키워드
콜백함수를 사용하기 위해서는 async와 await 키워드를 사용합니다.
async function foo(){
await someAsyncFunction(){...}
await anotherAsyncFunction(){...}
}
- 함수 이름 앞에 async 키워드
- 호출할 비동기 함수 앞에 await 키워드를 사용합니다.
핵심은 await이며, async는 단지 선언용입니다.
즉, 함수 앞에 async가 선언되어 있어야만 await이 적용됩니다.
위의 예제는 someAsyncFunction, anotherAsyncFunction 두 함수가 비동기 코드일지라도 async / await이 적용되면,
항상 someAsyncFunction -> anotherAsyncFunction 순서대로 함수가 실행됩니다.
이처럼 비동키 코드를 동기적으로 수행하게 해주는 것이 async / await입니다.
이 때 async/await은 Promise 방식을 사용하기 때문에 someAsyncFunction과 anotherAsyncFunction 함수는 Promise를 리턴해야 합니다.
이제 본격적으로 async/await이 정말로 Promise 방식을 사용하는지 살펴보면서, async/await 코딩 방법에 대해 알아보도록 하겠습니다.
2) 비동기 함수가 Promise를 사용하지 않는 예제 ( async / await 적용이 안된 예제 )
async function test(){
await foo(1, 2000)
await foo(2, 500)
await foo(3, 1000)
}
function foo(num, sec){
setTimeout( function(){
console.log(num);
}, sec);
}
test();
위의 예제는 비동기함수 setTimeout() 함수를 호출할 때 async/await을 사용해서 test() 함수 실행 결과 1,2,3 순서대로 응답되기를 기대한 것입니다.
그러나 1, 2, 3 순서대로 호출되지 않고, 이벤트가 끝난 순서대로 호출되었습니다.
즉, asycn/await으로 비동기 코드를 동기식으로 바꾸고 싶었지만 비동기 코드가 그대로 수행이 되었죠.
문법은 이게 맞는데 왜 그런걸까요?
다음 코드를 보겠습니다.
3) 비동기 함수에 Promise를 사용하는 예제 ( async / await이 적용되어 동기적으로 출력 )
async function test(){
await foo(1, 2000)
await foo(2, 500)
await foo(3, 1000)
}
function foo(num, sec){
return new Promise(function(resolve, reject){
setTimeout( function(){
console.log(num);
resolve("async는 Promise방식을 사용합니다.");
}, sec);
});
}
test();
이번에는 foo() 함수가 Promise를 반환하도록 수정했습니다.
그랬더니 의도대로 비동기 함수들이 순서대로 1, 2, 3을 응답했습니다!
그 이유는 await은 Promise를 받기 때문입니다.
즉, 비동기 함수 foo() 실행 결과 Promise를 리턴하면 await은 이를 받아 동기적으로 수행하게 해줍니다.
이로써 async/await은 Promise 방식을 따른다는 것을 확인했네요.
4) test함수를 Promise로 바꾸기
이번에는 async/await과 Promise 중 어느 것이 깔끔한 표현인지(누가 더 가독성이 좋은지) 비교하기 위해 위의 예제 test() 함수를 Promise로 바꿔 표현해보도록 하겠습니다.
function test(){
foo(1,2000)
.then( () => {
return foo(2,500)
})
.then( () => {
return foo(3,1000)
})
}
function foo(num, sec){
return new Promise(function(resolve, reject){
setTimeout( function(){
console.log(num);
resolve("async는 Promise방식을 사용합니다.");
}, sec);
});
}
test();
결과는 "3) 비동기 함수에 Promise를 사용하는 예제"와 같이 1, 2, 3 순서대로 출력됩니다.
코드가 복잡해질수록 async/await과 Promise 중 어느 방식이 더 가독성이 좋을까요?
사람마다 느끼는 것은 다르겠지만... 저는 async/await 방식이 더 간결해 보이네요.
5) async/await과 Promise 섞어쓰기
이번에는 async/await이 Promise를 사용한다는 점을 이용하여 Promise와 섞어서 사용해보도록 하겠습니다.
async function test(){
await foo(1, 2000)
await foo(2, 500).then( () => {
foo(3, 1000)
})
}
function foo(num, sec){
return new Promise(function(resolve, reject){
setTimeout( function(){
console.log(num);
resolve("async는 Promise방식을 사용합니다.");
}, sec);
});
}
test();
async/await과 Promise를 혼합해서 활용할 수 있다는 점을 보여주는 코드입니다.
그 이유는 async/await이 Promise를 반환하기 때문이죠.
3. 더 자세한 문법
앞의 예제들을 통해 async/await 사용법에 대해 알아보았습니다.
이번에는 async/await을 사용할 때 주의해야 할 문법과 표현에 대해 알아보도록 하겠습니다.
1) await은 aysnc함수 안에 포함되어야 합니다.
예제 - 올바르지 못한 문법
async function test(){
function goo(){
await foo(1, 2000)
}
await foo(2, 500)
await foo(3, 1000)
}
function foo(num, sec){
return new Promise(function(resolve, reject){
setTimeout( function(){
console.log(num);
resolve("async는 Promise방식을 사용합니다.");
}, sec);
});
}
test();
위의 예제를 실행하면 위와 같이 "await은 async 함수에서만 유효하다."는 에러가 발생합니다.
test() 함수 앞에 async를 선언했는데 무엇이 문제일까요?
test() 함수를 수정해보도록 하겠습니다.
예제 - 실행이 되긴 되는 방법 ( 올바른 결과 x )
async function test(){
async function goo(){
await foo(1, 2000)
}
goo()
await foo(2, 500)
await foo(3, 1000)
}
foo(1,2000) 함수의 await이 동작하기를 바라며 goo() 함수 앞에 async 키워드를 선언했습니다.
실행을 해보면 문제가 없어서 async/await이 적용된 것 같지만, 결과는 1,2,3이 순서대로 출력되지 않았습니다.
그 이유를 알아보기 위해 아래의 예제들을 더 살펴보도록 하겠습니다.
2) async 함수안에 async 함수
예제 - 실험1
// 코드 A
async function test(){
async function goo(){
await foo(1, 100)
}
goo()
await foo(2, 500)
await foo(3, 1000)
}
// 코드 B
async function test(){
async function goo(){
await foo(1, 700)
}
goo()
await foo(2, 500)
await foo(3, 1000)
}
코드 A에서 foo(1, 100) 함수를 호출했더니 순서대로 호출됐습니다.
그런데 코드 B에서 foo(1, 700)을 호출했더니 출력 순서가 또 바뀌었습니다.
이 예제를 통해 알 수 있는 것은 goo()함수와 나머지 비동기 함수가 경쟁을 하고 있다는 것입니다.
예제 - 실험2
async function test(){
async function goo(){
await foo(1, 700)
}
goo()
await foo(2, 500)
await foo(3, 100)
}
이번에는 0.5초와 0.1초로 설정하여 foo() 함수를 호출해보도록 하겠습니다.
여기서 눈여겨 볼 점은 goo() 함수와 나머지 비동기 함수는 경쟁을 하고 있지만, foo(2, 500) 함수와 foo(3, 100) 함수는 호출 순서를 유지하고 있다는 것입니다.
예제 - await으로 호출하면 해결 !
async function test(){
async function goo(){
await foo(1, 2000)
}
await goo()
await foo(2, 500)
await foo(3, 1000)
}
이제 정상적인 결과를 위해, goo() 함수에 await 키워드를 작성했습니다.
그러면 의도대로 1, 2, 3이 응답됩니다.
지금까지 예제들의 결론은 async 함수(test) 내부에 async 함수(goo)를 선언했을 때, goo함수 내부에 await을 선언하더라도 goo함수를 호출할 때 await을 선언해야 한다는 것입니다.
async/await을 사용하던 중 궁금해서 이것저것 실험했던 내용들을 주저리 주저리 설명했는데, 결론적으로는 바로 위의 코드만 기억하시면 될 것 같습니다.
3) async의 여러 표현
async의 기본 표현은 다음과 같았습니다.
async function test(){
await foo(1, 1000)
}
이를 즉시 실행 함수로도 표현할 수 있으며 다음과 같습니다.
(async function test(){
await foo(1, 1000)
})()
arrow 함수로도 표현할 수 있습니다.
let test = async () => {
await foo(1, 1000)
}
async 함수는 위와 같이 즉시 실행함수, arrow 함수로 표현이 모두 가능합니다 !
이상으로 async와 await에 대해 알아보았습니다.
비동기 함수를 다룰 때 사용하는 기법으로 3가지가 있는데요.
- callback 함수를 익명함수로 사용하는 방법
- Promise를 사용하는 방법
- async/await를 사용하는 방법
각각 장단점이 있으니 때에 따라 알맞게 사용하시면 좋을 것 같습니다.
'웹 프로그래밍 > ------ Javascript' 카테고리의 다른 글
[JS] assign 메서드 (0) | 2017.12.01 |
---|---|
[JS] for in와 for of 비교 - Iteration (0) | 2017.12.01 |
[JS] Arrow 함수 (0) | 2017.12.01 |
[JS] async/await으로 콜백지옥을 해결해보자 (7) | 2017.12.01 |
[JS] 이벤트 핸들링과 바인딩, 이벤트 흐름 (0) | 2017.11.29 |
[JS] 프로토타입(Prototype) 객체 (0) | 2017.11.22 |
[JS] 객체(Object)와 생성자 함수 (5) | 2017.11.22 |
[JS] this와 call(), apply() 메서드 (0) | 2017.11.20 |
잘 보고 갑니다 ~_~
감사합니다~~
넘 깔끔하잖아요 ㅡㅡ 감사합니다.
ㅋㅋㅋㅋ 감사합니다
goo 함수의 경우 Promise를 return하지 않는데도 await goo가 제대로 동작하는걸 보면,
await를 사용할 함수는 promise를 return하거나 asnyc로 선언이 되어있는 함수로 정리하면 되는걸까요?
async, await을 이해하는데 도움이많이 되었습니다 :) 감사합니다!
혹시 콜백 지옥을해결하는 포스팅 중에 해당글을 참고하였다는 url을 명시해도 될까요?
감사합니다~ 그럼요!