비동기 상황을 가장 쉽게 만들어 볼 수 있는 방법은 바로 타이머 API를 이용하는 방법입니다. 이미 Pre Course를 통해 setTimeout
setInterval
등의 타이머 API를 사용하는 방법을 익히셨을겁니다.
API가 뭔가요? API의 사전적 의미는 이후에 설명합니다. 지금은 일단 "사용할 수 있는 기능" 즉, 마치 식당에서 주문을 하기 위해 준비해 놓은 메뉴판 정도로만 이해해도 좋습니다. 브라우저라는 식당에서 타이머를 사용하려면
setTimeout
을 이용해 주문하면 되는거죠.
이번 시간에는 브라우저에서 사용하는 애니메이션 예제를 살펴봅니다. 브라우저에서 애니메이션을 구현하는 방법은 다양하지만, 기본적으로 시간에 따른 흐름이라는 인식에는 변함이 없습니다. callback과 Promise, 그리고 async
/await
키워드를 이용한 비동기 흐름 제어를 코드를 통해 이해해봅시다.
Part I은 모든 코드가 이미 작성되어 있습니다.
part-1/index.html
을 브라우저에서 열어보고, callback, promise, async & await 버튼을 각각 클릭해보세요. 이 버튼들은 정확히 동일한 액션을 하지만, 그 구현 방법은 조금씩 다릅니다.
어떤 함수가 실행되는지 part-1/script.js
파일을 열어 확인해보세요.
delay 함수와 sleep 함수의 차이점을 확인해보세요.
part-1/01_callback.js
파일, 그리고 part-1/02_promiseConstructor.js
파일에 각각 구현되어 있습니다. runAsync
함수는 async
및 await
키워드의 사용법을 보여주고 있습니다. 비동기 함수의 흐름이 마치 동기 함수를 사용하는 구조처럼 보입니다. 하지만, 실제로는 Promise를 이용하여 결과를 반환하는 것입니다. 이 경우, 코드 위에서부터 아래로, 시간의 흐름대로 진행됩니다.
Promise 실행함수가 가지고 있는 두 개의 파라미터, resolve, reject는 각각 무엇을 의미하나요?
resolve : 기능을 정상적으로 수행해서 최종적으로 데이터를 전달함
reject : 기능을 수행하다가 중간에 문제가 생기면, 호출하게 됨
const sleep = (wait) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve('hello')
}, wait);
});
}
c. runPromise 함수를 다음과 같이 바꿉니다.
function runPromise() {
resetTitle();
playVideo();
sleep(1000).then((param) => {
console.log(param);
pauseVideo();
displayTitle();
return 'world'
})
.then((param) => {
console.log(param);
sleep(500);
})
.then(highlightTitle)
.then(sleep.bind(null, 2000))
.then(resetTitle)
}
d. 브라우저 콘솔을 확인해서, 어떤 일이 발생했는지 확인해보세요.
Promise.prototype.then()
프로미스에 이행과 거부 처리기 콜백을 추가하고, 콜백이 호출될 경우 그 반환값으로 이행하며 호출되지 않을 경우(onFulfilled, onRejected 중 상태에 맞는 콜백이 함수가 아닐 경우) 처리된 값과 상태 그대로 처리되는 새로운 프로미스를 반환한다.
Promise.prototype.catch()
프로미스에 거부 처리기 콜백을 추가하고, 콜백이 호출될 경우 그 반환값으로 이행하며 호출되지 않을 경우, 즉 이전 프로미스가 이행하는 경우 이행한 값을 그대로 사용해 이행하는 새로운 프로미스를 반환한다.
a. 때로는 직접 눈으로 확인해보는 것이 이해하기 쉽습니다. 반드시 다음을 실습해보세요.
b. catch를 테스트해보고 싶다면 sleep 함수를 다음과 같이 고쳐봅시다.
const sleep = (wait) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('에러'))
}, wait);
});
}
c. runPromise 함수의 Promise 사용 부분에 catch메소드를 붙여봅니다.
// 생략
.then(resetTitle)
.catch(err => {
console.log(err);
})
대기(pending) : 이행하지도, 거부하지도 않은 초기 상태.
이행(fulfilled) : 연산이 성공적으로 완료됨.
거부(rejected) : 연산이 실패함.
state : pending -> fulfilled or rejected
const sleep = (wait) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve('hello')
}, wait);
});
}
c. 브라우저에서 개발자 콘솔을 열어 다음을 실행해본 후, returnValue에 담긴 값을 확인해보세요.
let returnValue = await sleep(1000);