[JS] Promise

김래영·2020년 1월 25일
0

Javascript

목록 보기
14/21

Asynchronus


자바스크립트는 비동기 처리를 위해 콜백 함수를 사용한다.
콜백 함수는 순차적인 처리가 많을 경우 가독성이 나쁘다.
또, 비동기 처리를 한꺼번에 처리하는 것도 한계가 있으며 에러 예외 처리에 대한 곤란한 부분이 있다.

Promise?

promise는 자바스크립트 비동기 처리에 사용되는 비동기 흐름을 제어해 주는 객체이다. 비동기 상태에서 주도권을 가지게 해준다.

콜백 함수의 단점


  • 가독성이 좋지 않다.
  • 에러처리에 한계가 있다.

콜백 지옥

지금까지 공부한 비동기 방식 콜백 함수는 아래와 같다.

function foo (callback) {
  setTimeout(function(){
    console.log('비동기 작업');
    callback();
  });
}
foo(function(){
  console.log('종료');
});

콜백 함수를 인자로 넣어주고 비동기 작업이 종료될 때 콜백 함수를 호출한다.
아래와 같이 비동기 처리 순서를 보장하기 위해 여러 개의 콜백 함수가 중첩되어 복잡도가 높아지는 콜백 지옥이 발생하는 단점이 있다. 콜백 지옥은 가독성이 좋지 않다

function foo (str, callback){
  setTimeout(function () {
    console.log(str)
    callback();
  }, 1000)
}

foo('1', function() {
  foo('2', function () {
    foo('3', function(){
      console.log('end')
    });
  });
});

에러 예외 처리

예를 들어, 인자로 숫자가 들어가고 인자를 더한 값을 반환하는 함수가 있다. 하지만 문자가 인자로 들어갔을 경우 에러를 발생 시키지 않는다.

function sum (a, b) {
  return a + b;
}
console.log(sum(1, 2));  // 3
console.log(sum('hello', 2));  // 'hello2'

아래와 같이 자바스크립트 문법적 오류가 없어 발생하지 않는 오류를 예외로 처리해 준다.

function sum (a, b) {
  if(typeof a !== 'number' || typeof b !== 'number'){
    throw '숫자가 아닙니다.'
  }
  return a + b;
}
console.log(sum(1, 2));   // 3
console.log(sum('hello', 2));   // error

try / catch / new Error

function foo () {
  console.log('3');
  throw new Error('에러')
  console.log('4');
}

function bar () {
  console.log('2');
  foo();
  console.log('5');
}

console.log('1');
bar();
console.log('6')

에러가 발생할 경우 실행되어야 할 함수들도 영향을 받아 모두 실행되지 않는다.
new Error에러 객체를 사용하면 error 부분의 콜스택 정보도 담긴다.

function foo () {
  console.log('3');
  throw new Error('에러')
  console.log('4');
}

function bar () {
  console.log('2');
  foo();
  console.log('5');
}

console.log('1');
try{
  bar();
}catch(err){
  console.log(err);
}
console.log('6')

try , catch를 사용할 경우 try 사이에서 발생한 error는 catch로 넘어간다. catch가 된 부분 뒤의 실행될 함수들은 영향을 받지 않고 실행된다.

비동기 에러 예외 처리

function foo () {
  setTimeout(function () {
    throw new Error('에러');
  }, 1000)
}

try {
  foo();
} catch (e) {
  console.log('에러를 캐치하지 못한다..');
  console.log(e);
}

try 블록 내에서 setTimeout 함수가 실행되면 1초 후에 콜백 함수가 실행되고 이 콜백 함수는 예외를 발생시킨다. 하지만 비동기 함수의 예외는 catch 블록에서 캐치되지 않는다. foo()함수가 실행될 때 비동기는 콜큐에 넣어지게 되므로 error를 잡지 못한다.

promise는 위와 같은 콜백 함수들의 단점들을 해결하고 주도권을 가져온다.

promise


Promise는 객체이다. Promise 생성자 함수를 통해 인스턴스화한다.
promise는 resolve와 reject를 함수의 인자로 전달받고, 비동기 처리가 성공(fulfilled)하였는지 또는 실패(rejected)하였는지 등의 상태 정보를 갖는다.

  • 대기(pending): 이행하거나 거부되지 않은 초기 상태.
  • 이행(fulfilled): 연산이 성공적으로 완료됨.
  • 거부(rejected): 연산이 실패함.

pendeing (대기)

new Promise(function (resolve, reject) {
  // 생략..
}); 

위와 같이 new Promise() 를 호출하면 Pending(대기) 상태가 된다. promise는 함수를 인자로 받으며 resolve와 resject를 함수의 인자로 전달받는다.

fulfilled (이행)

new Promise(function (resolve, reject) {
  setTimeout(function () {
    console.log('hello');
    resolve();
  }, 1000);
}); 

위와 같이 resolve를 실행하면 Fulfilled(이행) 상태가 된다. 비동기 작업이 성공적으로 완료되면 resolve를 호출한다. resolve는 한번만 호출된다.
그리고 이행 상태가 되면 Promise 객체의 후속 처리 메소드then, catch를 통해 비동기 처리 결과 또는 에러 메시지를 전달받아 처리한다.

then(), catch()

then(),catch는 Promise를 반환한다.

  • then()
    작업이 성공적으로 완료되면 resolve를 호출하고, then을 실행한다.
    then은 연속적으로 실행할 수 있다.
  • catch()
    에러가 발생했을 때 reject를 호출하고, catch는 에러를 잡아준다.
function foo () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log('hello');
      resolve();
    }, 1000);
  }); 
}

foo().then(function(){
  console.log('world');
});

// foo().then().then().then()  연속적으로 then을 사용할 수 있다. (chaining)

Rejected(실패)

reject() 실행하면 Rejected(실패) 상태가 된다.
실패 결과의 값을 catch()로 받을 수 있다.

function foo () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log('hello');
      reject(new Error("에러!"));
    }, 1000);
  }); 
}

foo().then(function(){
  console.log('world');
}).catch(function(err){
  console.log(err);
});

promise는 promise객체를 생성하고 then을 실행할 때 마다 실행된 새로운 promise객체를 반환한다.
예를들어,

function foo() {
  return new Promise(function(resolve, reject){
    setTimeout(function(){
      reject(new Error('에러!'));
    }, 1000);
  });
}

foo().catch(function(err){
  console.log('1 err ', err);   // 1 err 에러!
}).catch(function(err){
  console.log('2 err ', err)
});

위와 같은 결과가 나오는 이유는 무엇일까?
2 err1 err의 새로운 인스턴스 객체 값 받아온다.
1 err는 실행이 되었고 예외 처리가 없기 때문에 2 err가 실행되지 않는다.

Promise.all


Promise.all 은 순회 가능한 객체(배열)를 인자로 받는다.
순회 가능한 객체(배열)에 주어진 모든 promise를 이행한 후 then()을 호출 한다.
여러개의 비동기 작업들을 모두 이행한후 then함수의 다음 작업을 실행한다.

let a = new Promise(function(resolve, reject){
  setTimeout(function(){
    console.log('hello');
    resolve('1');
  }, 1000);
});

let b = new Promise(function(resolve, reject){
  setTimeout(function(){
    console.log('world');
    resolve('2');
  }, 1000);
});

Promise.all([a, b]).then(function(val){
  console.log(val)
});

// hello
// world
// [1, 2]

Promise.race


순회 가능한 객체(배열)를 인자로 받는다.
가장 먼저 완료된 것의 결과값으로 그대로 실행하거나 에러처리를 한다.

let a = new Promise(function(resolve, reject){
  setTimeout(function(){
    resolve('1')
  }, 1000);
});

let b = new Promise(function(resolve, reject){
  setTimeout(function(){
    resolve('2');
  }, 2000);
});

Promise.race([a, b]).then(function(val){
  console.log(val)
}).catch(function(err) { 
  console.log(err)
});

// 1 

profile
개발 노트

0개의 댓글