설모의 기록

Promise 본문

언어/TypeScript

Promise

HA_Kwon 2018. 1. 2. 22:38

  예전에 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가지 상태가 존재합니다.


promise states and fates

  1. pending : 대기

  2. fulfilled   : 이행

  3. rejected  : 거부


  아래는 기본적인 Promise 형태입니다.

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
Comments