서론
각각의 작업 a, b, c가 있다.
a는 5초가 걸리고, b는 10초, c는 3초가 걸린다.
작업을 순차적으로 기다리면서 처리하게 된다면 이 작업은 총 5+10+3 으로 총 18초가 걸리게 된다.
그럼 각각의 작업을 일괄적으로 처리하게 하려면 어떻게 해야 할까?
정답은 비동기 작업으로 해당 작업들을 수행하면 된다.
이 포스팅에서는 비동기 방법 중 Promise 객체에 대해 다룬도록 한다.
Promise
개념
- 비동기 작업의 완료 또는 실패에 따른 결과 값을 나타내는 객체
- 비동기 메서드에서 마치 동기 메서드처럼 반환할 수 있음
- 세 가지의 상태가 존재
- 대기(pending) : 초기 상태
- 이행(fulfilled) : 비동기 작업 성공
- 거부(rejected) : 비동기 작업 실패
- 상태에 따라 then 메서드에 의해 작업들의 처리 결과를 호출
기본 구문
기본 구문은 모질라 공식문서의 샘플 코드를 사용하였다.
let myFirstPromise = new Promise((resolve, reject) => {
// 우리가 수행한 비동기 작업이 성공한 경우 resolve(...)를 호출하고, 실패한 경우 reject(...)를 호출합니다.
// 이 예제에서는 setTimeout()을 사용해 비동기 코드를 흉내냅니다.
// 실제로는 여기서 XHR이나 HTML5 API를 사용할 것입니다.
setTimeout(function () {
resolve("성공!"); // 와! 문제 없음!
}, 250);
});
myFirstPromise.then((successMessage) => {
// successMessage는 위에서 resolve(...) 호출에 제공한 값입니다.
// 문자열이어야 하는 법은 없지만, 위에서 문자열을 줬으니 아마 문자열일 것입니다.
console.log("와! " + successMessage);
});
- Promise() : 새로운 Promise 객체를 생성하는 생성자, 주로 프로미스를 지원하지 않는 함수를 감쌀 때 사용
- Promise.resolve() : 비동기 작업이 처리된 결과 값이 포함된 Promise 객체를 반환, then을 사용할 경우 then 메서드에서 주어진 값을 반환
- Promise.reject(reason) : 비동기 작업이 실패한 Promise 객체를 반환
- setTimeout : 비동기적인 동작을 수행하는 함수, 숫자는 밀리초 단위 (1초 = 1000)
* resolve, reject는 생성자에서 주어진 인자(resolve, reject)를 사용하여 값을 반환할 수도 있다. 예) resolve("성공!")
async function
- 하나의 비동기 함수를 정의하여 AsyncFunction 객체를 반환하는 함수
- 암시적으로 Promise 객체를 사용하여 결과를 반환함
- 만약 반환값이 명시적으로 Promise가 아닐 경우, 암묵적으로 Promise로 감싸짐
- async 함수에는 await 키워드를 사용하여 동기 처리와 같은 방식으로 작업을 할 수 있음
- await 사용 시, 실행을 일시 중지하고 전달된 Promise의 결과를 기다린 다음 async 함수의 실행을 다시 시작하고 완료후 값을 반환함
- await 키워드는 async 함수에만 사용할 수 있음
- async 외의 함수에서 사용 시 Syntax Error 발생
기본 구문
기본 구문은 모질라 공식문서의 샘플 코드를 사용하였다.
async function name([param[, param[, ... param]]]) {
statements
}
- name : 함수의 이름
- param : 함수에게 전달되기 위한 인자의 이름
- statements : 함수 본문을 구성하는 내용, 이 곳에 await 키워드를 사용할 수 있음
예제1 : 비동기 작업 일괄 처리하기
각각의 작업을 모두 비동기로 일괄 처리해보자.
- 작업 A : 소요시간 5초
- 작업 B : 소요시간 10초
- 작업 C : 소요시간 3초
모두 작업을 일괄적으로 비동기로 처리하여야 하기 때문에
작업의 순서는 '작업C > 작업A > 작업B' 순으로 처리되어야 한다.
<script>
window.onload = function() {
// 5초 뒤 'a'를 반환하는 비동기 작업
workA();
// 10초 뒤 'b'를 반환하는 비동기 작업
workB();
// 3초 뒤 'c'를 반환하는 비동기 작업
workC();
};
function workA() {
return new Promise(function(resolve) {
setTimeout(function() {
resolve( console.log('a') );
},5000);
});
}
function workB() {
return new Promise(function(resolve) {
setTimeout(function() {
resolve( console.log('b') );
},10000);
});
}
function workC() {
return new Promise(function(resolve) {
setTimeout(function() {
resolve( console.log('c') );
},3000);
});
}
</script>
- 콘솔 확인 화면 : 정상적으로 c, a, b 작업이 순차적으로 진행되는 것을 확인할 수 있다.
예제2 : 비동기 작업을 동기로 처리하기
예제1번과는 다르게 특정 비동기 작업을 흐름에 맞게 동기적으로 처리해야하는 경우가 있다.
예를 들면, 로그인 세션을 확인하는 과정에서 세션 정보를 확인 후 페이지를 보여줘야 한다면?
세션 확인은 서버와 통신해야하기 때문에 비동기로 처리할 수 밖에 없다.
이런 경우, 세션을 받고 나서 작업을 수행하여야 한다.
그렇다면 예제1과 같은 작업에서 비동기로 처리된 작업을 동기적으로 진행해야 하는 경우를 알아보자.
- 작업 A : 소요시간 5초
- 작업 B : 소요시간 10초
- 작업 C : 소요시간 3초
위와 같은 경우라면 실제 실행되는 과정은 '작업A > 작업C > 작업B' 순으로 진행 되게 된다.
우리는 작업B가 포함된 전체 작업을 '작업A > 작업B > 작업C' 순으로 동기적으로 처리하려 한다.
우선 await 메커니즘을 살펴보자.
- 작업 A에서 5초를 소요하고 'a' 를 반환한다.
- 작업 B에서 비동기 작업으로 10초를 소요하여 'b'를 반환해야 하지만,
await 키워드를 사용하여 전체 작업을 일시 중지 하고 Promise의 반환을 기다린 다음 전체 작업을 다시 시작한다. - 작업 B의 Promise가 반환되면 작업 C에서 3초를 소요하고 'c'를 반환한다.
<script>
window.onload = async function() {
// 5초 걸리는 동기 작업
workA();
// 10초 걸리는 비동기 작업 : await를 사용하여 동기적으로 처리하도록 변경
await asyncWorkB();
// 3초 걸리는 동기 작업
workC();
};
// 5초 뒤 'a' 를 콘솔에 출력하는 함수
function workA() {
setTimeout(function() {
console.log('a');
}, 5000);
}
// 10초 뒤 'b' 를 콘솔에 출력하는 함수
async function asyncWorkB() {
return new Promise(function(resolve) {
setTimeout(function() {
resolve( console.log('b') );
},10000);
});
}
// 3초 뒤 'c' 를 콘솔에 출력하는 함수
function workC() {
setTimeout(function() {
console.log('c');
}, 3000);
}
</script>
- 콘솔 확인 화면 : 정상적으로 a, b, c 작업이 순차적으로 진행되는 것을 확인할 수 있다.
예제3. then을 사용하여 비동기 작업 처리하기
이번엔 then을 사용하여 비동기 작업을 수행하도록 해보자.
아래의 코드는 1초 뒤 비동기 작업이 성공된다는 메시지가 반환되는 코드이다.
<script>
window.onload = function() {
// Promise then 사용
asyncFunc()
// .then을 사용하여 주어진 결과를 반환할 수 있음
.then(result => {
console.log('결과:', result);
})
// .catch를 사용하여 에러를 제어할 수도 있음
// .catch(error => {
// console.error('오류:', error);
// });
};
// 비동기 작업 함수
function asyncFunc() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = "비동기 작업 성공";
resolve(data); // 성공 시 반환되는 값
}, 1000);
});
}
</script>
- 콘솔 확인 화면 : then을 사용하여 비동기 작업을 수행하고 then 메서드 내에 있는 콘솔 출력으로 값을 반환
비동기 작업의 성공, 실패 반환하기
비동기 작업에서 성공과 실패 상태를 확인하여 상황에 따라 값을 다르게 반환할 수 있다.
주로 에러를 확인하기 위해 사용하는데, 다음과 같은 코드를 통해 실패의 반환값을 확할 수 있다.
비동기 작업의 절차는 위에 모두 다뤘기 때문에 여기서 추가 설명은 따로 하지 않겠다.
window.onload = function() {
// Promise then 사용
asyncFunc()
.then(result => {
console.log('결과:', result);
})
.catch(error => {
console.error('오류:', error);
});
};
function asyncFunc() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = "비동기 작업 성공";
// 조건을 주어 결과에 따라 성공과 실패를 확인할 수 있다.
if (data > 0.5) { // 의도적으로 문자열을 숫자와 비교하여 실패하도록 함
resolve(data); // 성공
} else {
reject("비동기 작업 실패"); // 실패
}
}, 1);
});
}
- 콘솔 확인 화면 : 의도적으로 비동기 작업을 실패하여 실패 반환 값을 리턴 받는 경우