설모의 기록
Promise 본문
예전에 node.js 로 서버를 구현할 때 Promise 를 얼핏 보았는데 어떻게 사용하는지를 몰라서 넘겼던 것을 이제야 공부하고 정리하네요 :)
우선 아래의 예제를 보겠습니다.
import fs = require('fs');
function loadJSONSync(filename: string) {
return JSON.parse(fs.readFileSync(filename));
}
// json 파일이 정상적으로 존재할 때
console.log(loadJSONSync('good.json'));
// 파일이 존재하지 않아 loadJSONSync() 메소드가 에러를 발생할 때
try {
console.log(loadJSONSync('absent.json'));
}
catch (err) {
console.log('absent.json error', err.message);
}
// 잘못된 json 파일, 즉 파일은 존재하나 그 파일이 json 파일이 아니여서 loadJSONSync() 메소드가 에러를 발생할 때
try {
console.log(loadJSONSync('invalid.json'));
}
catch (err) {
console.log('invalid.json error', err.message);
}
주석과 try-catch 문이 난잡하게 구현되어있어 보기만해도 어지러운 코드입니다. 이럴 때 Promise 를 사용하면 코드를 훨씬 깔끔하게 구현할 수 있습니다.
Promise 는 비동기적인 코드의 에러를 동기적인 형태로 핸들링할 때 사용합니다. Promise 에는 3가지 상태가 존재합니다.
pending : 대기
fulfilled : 이행
rejected : 거부
new Promise((resolve, reject) => { });
Promise 생성자는 콜백메소드를 인자로 받아들입니다. 콜백메소드의 인자로는 resolve 함수와 reject 함수가 넘어옵니다. resolve 함수를 실행하면 promise 가 이행 (fulfilled) 되며, reject 함수를 실행하면 promise 가 거부 (reject) 됩니다.
Promise 의 메소드는 4가지가 있습니다.
- Promise.resolve(value) : value 값으로 결정된 promise 객체를 반환합니다. resolve() 를 호출하면 객체의 then() 메소드를 호출하며 value 는 then 의 인자로 들어가게 됩니다.
const promise = new Promise((resolve, reject) => {
resolve(123);
});
promise.then((res) => {
console.log(res === 123); // true
});
- Promise.reject(reason) : 인자로 넘어온 reason 으로 거부된 promise 객체를 반환합니다. reject() 를 호출하면 객체의 catch() 메소드를 호출해 catch 의 인자로 reason 을 보냅니다.
const promise = new Promise((resolve, reject) => {
reject(new Error("에러가 발생했습니다."));
});
promise.catch((err) => {
console.log(err.message); // '에러가 발생했습니다.'
});
- Promise.all(iterable) : iterable 내의 모든 promise 가 실행이 완료되면 이행되고 하나라도 거부된다면 즉시 거부하는 promise 를 반환합니다. iterable 은 promise 를 반환하는 배열입니다. 여러 promise 의 결과를 모두 모을 때 유용하게 사용할 수 있습니다.
function loadItem(id: number): Promise<{ id: number }> {
return new Promise((resolve, reject) => {
setTimeout(() => { // 서버 딜레이를 표현하기 위해 setTimeout() 메소드를 사용했습니다.
resolve({ id: id });
}, 1000);
});
}
Promise.all([loadItem(1), loadItem(2)])
.then((res) => {
[item1, item2] = res;
});
.catch((err) => {
console.log(err.message);
});
loadItem() 을 한 번 실행 시키는데 1초가 걸립니다. Promise.all(loadItem(1), loadItem(2)) 를 실행 시키는데 2초가 걸릴 것 같지만 두 loadItem() 이 평행하게 실행되기 때문에 2초가 아닌 1초가 걸리게 됩니다. 또한 두 loadItem() 이 모두 성공하면 두 메소드의 반환값이 배열로 then() 메소드의 res 인자에 넘어오게 됩니다. 따라서 item1 은 {id : 1} 이고 item2 는 {id : 2} 가 됩니다. 또한 둘 중 하나라도 실패하면 에러 내용이 catch() 메소드의 err 인자로 넘어오게 됩니다.
- Promise.race(iterable) : iterable 내의 모든 promise 중 하나라도 이행 또는 거부하자마자 then() 또는 catch() 로 넘어갑니다. 하나의 promise 가 넘어온 후 다른 promise 가 이행되거나 거부되어도 그것에 대한 then() 또는 catch() 가 실행되지는 않습니다.
var task1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 1000, 'one');
});
var task2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 2000, 'two');
});
Promise.race([task1, task2]).then(function(value) {
console.log(value); // "one"
// 둘다 resolve 가 실행되었어도, task1 이 빨랐으므로 task1 이 반환해준 promise 의 값인 one 이 출력된다.
});
Promise.prototype.then() 과 Promise.prototype.catch() 메소드 또한 promise 를 반환하기 때문에 chaining 형식으로 구현할 수 있습니다. 또한 then() 에서 에러를 반환하거나 reject() 를 호출하면 그 다음 catch() 메소드로 넘어가고, 그렇지 않으면 그 다음 then() 이 연이어 실행됩니다. 한번 catch() 문으로 들어가 에러 처리를 한 후 그 이후에도 then() 메소드가 존재한다면 chaining 은 계속됩니다. 아래의 예제를 통해 알아보겠습니다.
Promise.resolve(123)
.then((res) => {
throw new Error('something bad happened'); // throw a synchronous error
return 456;
})
.then((res) => {
console.log(res); // never called
return Promise.resolve(789);
})
.catch((err) => {
console.log(err.message); // something bad happened
})
.then((res) => {
})
.then((res) => {
return Promise.resolve(789);
})
.catch((err) => {
console.log(err.message); // something bad happened
})
위의 실행순서는 첫번째 then() -> 첫번째 catch() -> 세번째 then() -> 네번째 then() -> 다섯번째 then() 이 호출되고 마무리 됩니다. then() 메소드 또는 catch() 메소드가 반환하는 값이 없더라도 함수는 기본적으로 undefined 를 반환하기 때문에 다음 chain 값으로 넘어가게되며 인자값에는 undefined 가 넘어옵니다.
이상으로 es6 의 Promise 를 알아보았습니다.
참고사이트 : https://basarat.gitbooks.io/typescript/content/docs/promise.html
'언어 > TypeScript' 카테고리의 다른 글
Symbol (0) | 2018.01.03 |
---|---|
Classes Emit (0) | 2017.12.28 |
Classes Super (0) | 2017.12.28 |
TypeScript Class (0) | 2017.12.28 |
TypeScript Index Signature (0) | 2017.12.28 |