[JavaScript] 비동기 처리방법, Promise

muz·2022년 1월 11일
0
post-thumbnail

비동기를 처리하는 방법 Promise

프로미스는 JavaScript안에 내장된 Object이다. 이는 비동기를 간편하게 처리할 수 있도록 도와주는 Object로, 콜백함수 대신 사용할 수 있다. 정해진 장시간의 기능을 수행한 후 정상적으로 기능이 수행되어진다면 성공의 메시지와 함께 처리된 데이터를 제공하고, 만약 기능을 수행하다가 예상치 못한 오류가 발생하면 에러를 전달한다.

Promise의 2가지 Point 🙌🏻

1. state - 3가지 상태 구분하기

: promise가 만들어져서 지정한 오퍼레이션이 수행중일때에는 Pending 상태가 되고, 해당 오퍼레이션이 성공적으로 끝나면 바로 fulfilled 상태가 된다. 예기치 못한 오류(파일을 찾을 수 없거나 네트워크에 문제가 생긴다면)가 발생한다면 rejected 상태가 된다.

때문에 프로세스가 무거운 연산(operation)을 수행하고 있는지, 연산이 모두 완료되어서 성공했는지 or 실패했는지에 대한 상태를 이해하는 것이 중요하다.

2. Producer와 Consumer의 차이점 이해하기

: 원하는 데이터를 제공하는 사람(Producer)와 이 제공된 데이터를 쓰는 사람(Consumer)의 차이점을 이해하는 것이 중요하다.

자세히 알아보기 📚

producer ?

promise는 class여서 new 키워드를 사용해 원하는 Object를 생성할 수 있다.

해당 사진을 보면 알 수 있듯이, Promise의 생성자에 executor 라는 콜백함수를 전달해주어야한다. 이 콜백함수는 또 다른 두가지의 콜백함수를 받는다.

  1. resolve
    : 기능을 정상적으로 수행해서 마지막에 최종 데이터를 전달하는 콜백함수

  2. reject
    : 기능을 수행하다가 중간에 문제가 생기면 호출하게 될 콜백함수

보통 Promise 내에서는 조금 해비한 일들을 한다. 주로 네트워크에서 데이터를 받아오거나, 파일에서 큰 데이터를 읽어오는 작업을 한다. 이러한 작업들은 시간이 꽤 걸리는 작업으로, 이를 동기적으로 처리하게 되면 해당 작업을 하는 동안 그 다음라인의 코드가 실행되지 않게 된다. 때문에 시간이 다소 걸리는 작업들은 promise를 만들어서 비동기적으로 처리하는 것이 좋다.

아래의 코드는 프로미스를 생성하고, executor로 resolve와 reject 콜백함수를 전달한다. 네트워크 통신을 하는 것처럼 보이기 위해 2초간 어떤 일을 하다가, 결과를 주는 코드로 작성했다.

const promise = new Promise((resolve, reject) => {
  console.log('doing something...');
  
  // 네트워크 통신을 하는 것처럼 setTimeout으로 딜레이 주기
  setTimeout(() => {
    resolve('muz');
    // reject(new Error('no network'));
  }, 2000);
});

😤 중요해요!
promise가 생성되어질 때에는 전달한 executor 콜백함수가 바로, 자동적으로 실행된다.

위의 코드를 보면, new 키워드로 promise를 생성하는 순간 즉시실행함수로 인해 executor 콜백함수가 바로 실행되는 것을 알 수 있다. promise안에 네트워크 통신을 하는 코드를 작성했다면, promise가 만들어지는 순간 바로 네트워크 통신을 수행하게 된다는 것이다.

만약 네트워크 요청이 사용자가 요구하는 경우에 일어나야 하는 것이라면(ex. 사용자가 버튼을 눌렀을 때 네트워크 통신이 일어나는 경우), 사용자가 요구하지 않았음에도 불필요한 네트워크 통신이 일어날 수 있다는 것이다.

위의 코드에서 성공적으로 일이 마무리되면 resolve를 통해 데이터를 전달하고, 예기치 못한 에러가 발생하게되면 Error Object를 통해 에러 값을 전달한다. 이 Error Object는 JS에서 제공하는 Object중 하나이다.

Consumer ?

위의 producer 부분에서 작성한 promise를 사용해보자. consumer는 then, catch, finally 를 통해 값을 받아올 수 있다.

promise
	.then(value => {
  		console.log(value);
    })
    .catch(error => {
      	console.log(error);
    })
    .finally(() => {
      	console.log('finally');
    });
  1. then
  • then은 promise가 정상적으로 잘 수행되어서 마지막에 최종적으로 resolve 콜백함수를 통해 전달한 값이 value의 파라미터로 전달되어 들어온다.
  • promise에 then을 호출하게되면, 이 then은 결국 똑같은 promise를 return한다.
  1. catch
  • 만약 promise에서 resolve 대신 reject를 호출하게 되면, 콘솔에서 에러를 만나게 된다.
  • reject는, catch를 이용해 에러가 발생했을 때 이를 어떻게 처리할 지 콜백함수를 등록할 수 있다.
  • 위의 then에서 return한 promise에 catch를 다시 호출할 수 있다. (promise chaining)
  1. finally
  • then과 catch를 이용해서 성공한 값, 실패한 에러를 받아와서 원하는 방식으로 처리한 뒤,
    성공, 실패 여부에 상관없이 어떤 기능을 마지막에 수행하고 싶다면 finally를 사용할 수 있다.

🍩 요약
promise를 생성하고 나서 그 안에 비동기적으로 처리되었으면 하는 기능들의 코드들을 작성한다. 성공하면 resolve를 호출하게하고, 실패했으면 reject를 통해 왜 실패했는지 그 이유와 에러를 전달하는 것이다.

Promise Chaining

서버에서 숫자를 받아오는 새로운 promise를 만들어보자. 서버에서 직접 받아오는 건 없으니 setTimeout으로 적당히 통신이 일어난다고 가정해보자.

1초 뒤에 숫자를 전달해주는 promise를 만들어보자.

const fetchNumber = new Promise((resolve, reject) => {
  setTimeout(() => resolve(1), 1000);
});

fetchNumber
	.then(num => num*2) // 1 => 1*2=2
	.then(num => num*3) // 2 => 2*3=6
	.then(num => { // 6 =>
  		return new Promise((resolve, reject) => {
          setTimeout(() => resolve(num-1), 1000); // 6-1=5
        });
})
.then(num => console.log(num)); // 5

then은 값을 바로 전달할 수도 있고, 또 다른 비동기인 promise를 전달할 수도 있다.

Error Handling

promise를 chaning했을 때, 에러는 어떻게 해주어야 할까? 에러 핸들링 방법에 대해 알아보자.

// producer
const getHen = () => new Promise((resolve, reject) => {
  setTimeout(() => resolve('🐓'), 1000);
});

const getEgg = hen => new Promise((resolve, reject) => {
  setTimeout(() => resolve(`${hen} => 🥚`), 1000);
});

const cook = egg => new Promise((resolve, reject) => {
  setTimeout(() => resolve(`${egg} => 🍳`), 1000);
});

// consumer
getHen()
	.then(hen => getEgg(hen))
	.then(egg => cook(egg))
	.then(meal => console.log(meal))
	.catch(console.log);

위의 코드를 실행하면 Promise가 반환되고, 그 다음 콘솔에 🐓 => 🥚 => 🍳 이 출력된다.

만약 egg를 받아올 때 문제가 발생하면 요리를 하지 못한채 마무리될 것이다. getEgg 함수를 아래의 코드처럼 수정한 후 다시 실행보자.

// producer
const getEgg = hen => new Promise((resolve, reject) => {
  setTimeout(() => //resolve(`${hen} => 🥚`), 1000);
             reject(new Error(`error! ${hen} => 🥚`)),1000);
});


에러가 발생해서 요리를 하지 못한채 마무리 되었다. egg를 받아오지 못하면 egg대신 빵을 재료로 이용할 수 있게끔 에러를 핸들링해보자. 즉, egg를 받아오지 못해 error가 발생하면 egg대신 빵을 리턴해줌으로써, 전체적인 promise chain에 문제가 발생하지 않도록 처리를 해보자.

// consumer
getHen()
	.then(hen => getEgg(hen))
	.catch(error => {
  		return '🍞'
	})
	.then(egg => cook(egg))
	.then(meal => console.log(meal))
	.catch(console.log);

profile
Life is what i make up it 💨

0개의 댓글