본문 바로가기
JavaScript 기초/비동기

Promise와 fetch API (1) Promise란

by 개미는뚠뚠딴 2020. 11. 11.
반응형

오늘은 Promise와 fetch API에 대해서 공부했다. 

Promise는 자바스크립트에서 비동기 처리를 위한 하나의 패턴으로 콜백 패턴의 단점을 보완해준다.

(콜백 패턴은 처리 순서를 보장하기 위해 중첩된 괄호를 적어야 하는데 이로 인해 복잡해지고 가독성도 안 좋을뿐더러 실수를 유발하기도 한다. 또한 에러 처리도 어려워 콜백 패턴의 한계를 느끼고 새롭게 Promise패턴을 도입하게 된 것이다.)

이제 Promise의 사용법을 알아보자.

const getDataFromFilePromise = filePath => {
  return new Promise((resolve, reject) => {
    fs.readFile(filePath, 'utf-8', (err, data) => {
      if (err) { // 에러 발생
        reject(err);
      } else {
        resolve(data); // 성공적
      }
    });
  });
};

위 코드는 파일을 읽는 코드인데 간단한 예시를 통해 Promise 사용방법에 대해서 알아보았다.
Promise는 두 개의 인자를 받는데 보통 첫 번째 인자로는 성공했을 경우, 두 번째 인자로는 실패했을 경우(오류 발생)이다.

첫 번째 인자로 resolve를 넣어주고 비동기 작업의 수행이 성공했을 경우 resolve 함수에 성공한 값을 넣어준다. 

Promise는 세 가지 상태를 갖는데 다음은 Promise의 세 가지 상태이다.

  • pending : 비동기 처리가 아직 수행되지 않은 상태로 reject 또는 resolve모두 호출이 안된 상태이다.
  • fulfilled: 비동기 처리가 수행된 상태 중 성공적으로 수행된 상태로 resolve함수가 호출된 상태이다.
  • rejected: 비동기 처리가 수행된 상태 중 예외가 발생한 상태로 rejected함수가 호출된 상태이다.

 

다음은 Promise의 후속 처리 메소드이다. Promise의 후속 처리 메소드로는 then과 catch가 존재한다. 


then은 반환된 Promise 객체의 결과를 받아 후속 처리를 해주는데 이는 체이닝 방식으로 호출한다.
then의 첫번째 인자로는 성공 시 호출되는 콜백 함수이고 두 번째 인자는 실패 시 호출되는 콜백 함수이다. (에러 처리 시 사용)
then 은 Promise를 반환하여 이를 체인 형식으로 연결해주면 여러 개의 비동기 함수를 처리할 수 있다.
catch는 예외가 발생하면 호출되는데 catch도 Promise를 반환한다.

const readAllUsersChaining = () => {
  // TODO: 여러개의 Promise를 then으로 연결하여 작성합니다
  return getDataFromFilePromise(user1Path)
    .then((item1) => {
      return getDataFromFilePromise(user2Path)
      .then ((item2) => {
        let arr = []; 
        arr.push(JSON.parse(item1));
        arr.push(JSON.parse(item2));
        return arr;
      }, err => console.err(err)) // then의 두번째 인자로 err을 처리해줄 콜백함수를 넣어줌
    })
    .catch(err => console.err(err)); // error 처리
}

 

위의 코드는 Promise 체이닝(.then으로 앞에서 처리된 Promise를 받아 Promise를 또 연결)을 이용하여 여러 개의 Promise를 처리한 코드이다. 이런 식으로 에러 처리가 가능하다 then의 첫 번째 인자로 Promise의 후속 처리를 두 번째 인자로는 에러 처리를 해주면 된다.
그러나 이렇게 에러처리를 해줄 경우 then마다 에러 처리를 해줘야 하고 가독성도 안 좋아진다.
그래서 catch를 사용한다. catch에서 마지막에 에러를 한 번에 잡아주기 때문에 위에서 어떠한 에러가 발생한다면 바로 catch문으로 내려오게 된다.

다음으로 Promise.all에 대해서 알아보겠다. 

Promise.all은 인자로 순회가 가능한 구조의 Promise가 담긴 배열을 인자로 받는다. 여러 개의 Promise들을 병렬적으로 처리할 수 있기 때문에 아주 유용하다.
또한 처리된 결과들을 배열로 반환해주기 때문에 Promise가 처리된 순서도 보장이 된다.

const readAllUsers = () => {
  let arr = [];
  return Promise.all([getDataFromFilePromise(user1Path), getDataFromFilePromise(user2Path)])
  .then(([value1, value2]) => { 
  /* 배열로 반환 -> 하나의 변수만 들어있으면 배열이 되는데 []안에 변수 이름으로 적으면 
  구조분해 할당을 이용해 각 promise의 결과값에 변수명을 붙일 수 있다. */
    arr.push(JSON.parse(value1))
    arr.push(JSON.parse(value2))
    return arr;
  }) 
}

Promise.all의 인자로 여러 개의 Promise를 넣어준 후 이 결과값을 then에서 받아준다. 배열 형태로 받아주는데 Promise 배열의 인덱스와 결과값의 인덱스는 일치한다.(실행 순서 보장)

Promise.all 말고도 여러개의 Promise를 처리하는 방법이 있다. 바로 async, await인데 이것을 활용하면 Promise.all처럼 Promise를 변수에 담아 사용이 가능하며 병렬적 수행과 실행 순서도 보장된다.

const readAllUsersAsyncAwait = async () => {
  let arr = [];
  let item1 = await getDataFromFilePromise(user1Path); // getDataFromFilePromise()는 Promise를 처리하는 함수
  let item2 = await getDataFromFilePromise(user2Path);
  arr.push(JSON.parse(item1));
  arr.push(JSON.parse(item2));
    
  return arr;
}

함수 앞에 async를 넣어주고 비동기로 처리해줄 (Promise를 반환 하는)함수 앞에 await를 넣어준다. 
Promise.all이 없이 여러개의 Promise를 다룰 수 있음이 보인다.

다음에 알아볼 fetch API는 Promise형식으로 이루어져 사용법이 Promise와 매우 유사하다.
글이 길어져서 fetch API는 다음 글에서 다루도록 하겠다.

반응형

'JavaScript 기초 > 비동기' 카테고리의 다른 글

Promise와 fetch API (2) fetch API란  (0) 2020.11.11

댓글