지난 시간에 콜백함수보다 개선된 디자인 패턴인 Promise에 대해서 알아봤다.
let 프로미스 = new Promise(function(성공, 실패){
setTimeout(function(){
성공();
}, 1000);
});
프로미스.then(function(결과){
console.log('성공했어요');
}).catch(function(){
console.log('실패했어요');
});
하지만, Promise가 어렵다면 그보다 훨씬 쉽게 쓸 수 있는 ES8(2017) 문법이 있다.
async, await이라는 키워드인데 각각 Promise와 then을 매우 쉽게 만들어주는 문법이다.
함수를 하나 만든다고 치자!!
function 더하기 (){
1 + 1
}
이 때, 연산이 성공적으로 끝나고 특정 코드를 실행하고 싶다면, 어떻게 할 수 있을까?
콜백 함수를 디자인할 수있을 거다.
function 더하기 (){
1 + 1
콜백();
}
더하기(어떤함수);
그러면, 더하기();
함수를 실행하고 나서 그 자리에다가 뭔가 함수를 또 입력해가지고 순차적으로 실행할 수있을 거다.
더하기(어떤함수);
// 1 2
더하기 함수를 실행하고, 2번 함수를 실행해주세요.
그런데, 이런 콜백 패턴이 길어지는게 싫어서 Promise 디자인 패턴으로
더하기().then(function(){
})
이렇게 만들고 더하기()
함수를 Promise로 만들어내면 된다.
=> 더하기()
가 하는 연산을 Promise 안에다가 집어넣든가 하면 된다.
근데, 이러면 Promise라는 걸 하나 또 만들어야 되잖아요!!
그게 귀찮다. 그러면, function
에다가 async
라는 키워드 하나만 붙이면 된다.
이것은 함수 선언 앞에다가만 붙일 수 있는 특별한 키워드이다.
화살표 함수 앞에다가 붙여도 된다.
async 함수 실행후에는 Promise 객체
가 남는다.
더하기().then(function(){
// 이 자리에 함수 실행 후, Promise 객체가 남는다는 말이다.
})
Promise 객체
가 남으면, 뭘 할수 있죠??.then()
구문을 쓸 수 있다.
// 이제 콜백 함수나 Promise 같은 거 디자인 안 해도
// .then 체인을 사용할 수있다.
async function 더하기(){
1 + 1
}
더하기().then(function(){
console.log('성공');
})
async
만 붙이면, Promise 객체를 뱉어내고, Promise처럼.then()
을 붙일 수가 있다.
async function 더하기(){
return 1+1; // 결과를 출력해줄 수 있다
}
더하기().then(function(결과){
console.log(결과);
});
이게 async
라는 최신 문법의 사용법 이라고 보면 된다.
async function 더하기(){
return Promise.reject('실패임');
// 이러면, .then 구문은 더이상 실행되지 않는다.
// 에러를 뿜어냄!!
}
더하기().then(function(결과){
console.log(결과);
})
내가 Promise
를 정확히 잘 쓰고 싶어서, 함수 안에서 그냥 Promise
를 직접 디자인한다고 가정해보자!!
async function 더하기 () {
let 프로미스 = new Promise(function(성공, 실패){
let 힘든연산 = 1+1;
성공();
// 이렇게 성공 판정을 내리면,
// .then 으로 넘어가고 잘 끝나기는 한다.
})
}
그런데, .then
보다 async
안에서는 Promise
를 좀 더 깔끔하게 쓸 수있다.
물론, async
안에 .then
을 넣을 수도 있다.
async function 더하기 () {
let 프로미스 = new Promise(function(성공, 실패){
let 힘든연산 = 1+1;
성공();
// 이렇게 성공 판정을 내리면,
// .then 으로 넘어가고 잘 끝나기는 한다.
});
////// 이 부분을 좀 더 예쁘게 쓸 수있다.
프로미스.then(function(){
console.log('성공했어요');
});
}
////////
더하기();
.then()
과 똑같은 의미로 쓸 수있는 키워드가 바로 await
이라는 키워드이다.
JS에서는 어렵고 힘든 연산을 만나면, 비동기식으로 처리해줄 수있다.
=> 약간 제겨두고 무언가를 실행시켜줄 수 있다는 뜻이다.
그런데, 그렇게 비동기식으로 처리되는 코드들 위아래에 뭔가 순차적으로 실행되는 코드가 있다면, 실행결과가 꼬이는 수가 있다.
=> 즉, 뭐가 먼저 실행될지 알 수 없다는 말이다.
이럴때, await
이라는 키워드를 쓰면, 순서를 개발자들이 의도한대로 컨트롤할 수가 있다.
await
은 " Promise가 해결되기를 기다려~ " 라는 뜻이다.
JS는 코드가 위에서부터 한 줄 한 줄 실행하다가,
let 결과 = await 프로미스;
를 만나면, await
을 발견하는 순간, 비동기 처리하지 않고 기다린다. 프로미스가 해결될 때까지...
그래서, 성공이든 실패든 뭔가 판정이 나오기를 기다린다.
그래서, " 성공 판정이 나왔다 " 하면, 그 결과를 변수에 할당해준다.
await
을 이용해서, 프로미스 연산 결과를 변수에 할당하는 게 가능하다
날씨API Sprint 할 때, json이라는 변수에 undefined 나왔던거 생각하면, await
키워드를 사용하면, 값이 제대로 나오겠다.
.then
이라는 구문보다 await
이 직관으로 이해하기 더 쉽고, 파이썬 같은 다른 언어를 다루는 개발자들이 봐도 납득이 가는 방식이다.let 결과 = await 프로미스;
undefined
가 안 나오고, 값이 할당될 때까지 기다림그러면, async
라는 걸 함수에 써보자!!
await
라는 키워드는 익숙해지면, 되게 쉽다.
=> 개발자가 Promise
를 조금 더 예쁘게 다룰 수가 있다.
let 결과 = await 프로미스;
console.log(결과);
는
프로미스.then(function(){
console.log("성공");
});
의 간략한 버전 그 이상도 이하도 아니다.
무슨 신기술 같이 전혀 특별한 게 아니다.
그런데, await
에 실패값을 출력해보자!!
Promise가 실패하는 하단 코드를 실행해봅시다.
async function 더하기(){
var 어려운연산 = new Promise((성공, 실패)=>{
실패();
});
var 결과 = await 어려운연산;
console.log(결과);
}
더하기();
어려운연산이라는 Promise가 실패할 경우
await 어려운연산이라는 코드는 에러가 나고 코드실행을 멈춘다.
그럼 await 하단에 있는 코드들은 더 이상 실행이 되지 않겠죠.
그래서 Promise가 실패할 경우
코드실행을 멈추고 싶지 않으면 약간 특별한 방법이 필요하다.
async function 더하기(){
var 어려운연산 = new Promise((성공, 실패)=>{
실패();
});
try { var 결과 = await 어려운연산 }
catch { 어려운연산 Promise가 실패할 경우 실행할 코드 }
}
try
catch
라는 자바스크립트 문법인데,
try {}
안의 코드가 에러가 나고 멈출 경우
대신 catch {}
내부의 코드를 실행해준다.
try{
이걸 해보고
}
catch{
안되면(에러가 나면),
이걸 실행해주세요~
}
try/catch
구문으로 싸매면, 코드 멈춤없이 실행할 수있다.
try{
let 결과 = await 프로미스;
console.log(결과);
}
catch{
console.log('프로미스 연산이 잘 안되었군요');
}
try/catch
는 await
과 자주 함께 쓰이는 문법이다.
이렇게 에러처리를 하실 수 있다.
안 써도 된다.
더 복잡하니까 그냥 then()
이런거 쓰셈!!
근데, await
이라는 키워드가 조금 더 보기가 쉽잖아요.
어려운연산이라는 Promise가 실패()
가 안날거라고 자신하면 try/catch
를 굳이 쓸 필요는 없으니 코드가 더 간단해질 수도 있다.
<button>
을 누르면 성공하는 Promise 만들기Q. HTML 페이지 내에 버튼 아무거나 하나 만들고 그걸 클릭하면 성공하는 Promise를 만들고 싶습니다.
성공하면 콘솔창에 '성공했어요'를 출력하고요.
어떻게 코드를 짜면 될까요?
(async, await이 필요하면 써봅시다)
<button id="test">버튼</button>
<script>
var 프로미스 = new Promise(function(성공, 실패){
document.getElementById('test').addEventListener('click', function(){
성공();
});
})
async function 버튼누르기(){
var 결과 = await 프로미스;
console.log('성공했으요')
}
버튼누르기();
</script>
전 이렇게 짰습니다.
Promise
를 만들었습니다.그건 별거 아닙니다.
console.log()
를 해주는 코드를 짜려고 봤더니 then
을 쓰기 싫어서await 프로미스;
이렇게 작성했습니다.
await
을 쓰려면 async functinon
안에서만 쓸 수 있댔죠?그래서 await 프로미스;
코드를 async function
을 하나 만들어서 감쌌을 뿐입니다.
혹은 이렇게 짜셨을 수도 있겠군요.
<button id="test">버튼</button>
<script>
async function 프로미스(){
document.getElementById('test').addEventListener('click', function(){
return '성공했어요'
});
}
async function 버튼누르기(){
var 결과 = await 프로미스();
console.log(결과)
}
버튼누르기();
</script>
▲근데 위의 코드는 잘 동작하지 않습니다.
async가 Promise를 퉤 뱉는다고해서 async function 프로미스()
를 쓰고 이벤트리스너를 안에 담긴 했는데
버튼 누르면 return 어쩌구에 의해서 성공판정이 될거라고 기대했지만 안됩니다.
실은 return '성공했어요' 이게 async function
의 return이 아니고 이벤트리스너안의 함수의 return문이라 문제되는 것도 있지만
그것보다 더 중요한 문제는 이겁니다.
이벤트 리스너안의 코드는 바로 실행되지 않습니다. 버튼 누를 때 실행됩니다.
그래서 컴퓨터가 코드를 쭉 읽을 때 async function 프로미스() 함수
내부는 빈칸과 동일합니다.
자바스크립트는 function 안이 빈칸이면 그냥 자동으로 return undefined
를 채워 실행합니다.
(그럼 3번에 의해서 async function 프로미스()
는 0초만에 자동으로 성공()
판정이 됩니다)
그래서 하단의
var 결과 = await 프로미스();
이 코드는 프로미스()
가 0초만에 성공판정이 내려진 상태로 실행되며 (그 함수에 return undefined가 자동으로 채워지니까요)
var 결과 = undefined
와 동일한 뜻입니다.
그래서 코드가 이상해진 것입니다.
하지만 Promise로 만들어서 직접 성공()
, 실패()
경우를 지정해준다면
await이 잘 기다려줍니다.
구지 필요는 없다. 그냥 콜백함수로 잘 해결할 수 있다.
BUT, 나중 가서 필요한 상황이 올 수 있다.
코드가 길거나 복잡해지면, async & await
이 조금 더 유용해질 수 있다.
특히, 순차적으로 많은 것을 실행하고 싶을 때, async & await
은 유용하다.