[JavaScript] Promise 객체 한 번에 이해하자!

MINEW·2022년 11월 12일
0

Promise 객체는 무엇일까?

  1. Promise 객체 란?
    - 어떤 작업에 관한 '상태 정보'를 갖고 있는 객체입니다.
    - 작업의 결과가 promise 객체에 저장됩니다. promise 객체를 보면 작업이 성공했는지, 실패했는지 알 수 있습니다.

  2. promise 객체의 3가지 상태
    - promise 객체는 3가지 중 1개의 상태를 가집니다.
    - 작업이 진행중임을 의미하는 pending.
    - 작업이 성공적으로 완료되었음을 의미하는 fulfilled. 이때, 작업이 성공해서 promise 객체가 fulfilled 상태가 되면, promise 객체는 '작업의 성공 결과'도 함께 갖게됩니다. (작업 성공 결과)
    - 작업이 실패했음을 의미하는 rejected. 이때, 작업이 실패해서 promise 객체가 rejected 상태가 되면, promise 객체는 '작업의 실패 이유'에 관한 정보도 함께 갖게됩니다. (작업 실패 정보)
    - 참고로, pending 상태에서 한번 fulfilled 상태 또는 rejected 상태가 되면 다시 다른 상태를 가질 수 없습니다.

  3. 왜 사용할까?
    - 비동기 작업을 순차적으로 실행하기 위해서 사용합니다.
    - callback 함수가 많아지면 속칭, callback 지옥에서 빠져 가독성이 매우 낮아집니다. callback 함수를 보기좋게 하기위해서 Promise를 사용합니다.
    - 그러나, 비동기적 처리가 가능하게 바꿔주는 문법이 아닌, 단지 callback 함수 대신해서 보기 쉽게 만들어주는 것이라는 사실을 잊어서는 안 됩니다.

  4. promise의 메서드
    - promise는 then, catch, finally 3가지 메서드를 가집니다.
    - then: promise 객체가 fulfilled 상태가 되면 실행할 콜백함수를 등록하는 메서드 입니다.
    - catch: promise 객체가 rejected 상태가 되면 실행할 콜백함수를 등록하는 메서드 입니다.
    - finally: 어떤 작업의 성공, 실패 여부와 상관없이 항상 실행하고 싶은 콜백함수를 등록하는 메서드 입니다.

fulfilled, rejected 코드 예시

  1. fulfilled 기본 예시 1 (with then)
fetch('https://jsonplaceholder.typicode.com/users')
  .then((response) => response.json()) // 1번) promise 객체에 then 메서드를 연속적으로 붙이는 것을, 프로미스 체이닝 이라고 한다.
  .then((data) => {
    const users = data;
    return users[0]; // 2번) then 메서드는 새로운 promise 객체를 리턴한다.
  })
  .then((user) => { // 3번) 즉, user 라는 새로운 promise 객체를 받는 것이다.
    console.log(user);
    const { address } = user;
    return address;
  })
  .then((address) => {
    console.log(address);
    const { geo } = address;
    return geo;
  })
  .then((geo) => {
    console.log(geo);
    const { lat } = geo;
    return lat;
  })
  .then((lat) => {
    console.log(lat);
  });
  1. fulfilled 기본 예시 2 (with then)
fetch('https://learn.codeit.kr/api/interviews/summer')
  .then((response) => response.json())
  .then((interviewResult) => {
    const { interviewees } = interviewResult;
    const newMembers = interviewees.filter((interviewee) => interviewee.result === 'pass');
    return newMembers;
  })
  .then((newMembers) => fetch('https://learn.codeit.kr/api/members', {
    method: 'POST',
    body: JSON.stringify(newMembers)
  }))
  .then((response) => { 
    if (response.status === 200) {
      return response;
    } else {
      throw new Error('New members not added');
    }
  })
  .then((response) => response.json())
  .then((members) => {
    console.log(`총 직원 수: ${members.length}`);
    console.log(members);
  });
  1. rejected 탈을 쓴 fulfilled 기본 예시 1 (with then)
fetch('https://jsonplaceholder.typicode.com/users')
  /*
    1번) promise 객체가 실패하면 실행하고 싶은 콜백함수는, then 메서드의 2번째 파라메터로 넣으면 된다.
    실패하면, error에는 작업 실패 정보가 들어가게 된다.
    주의사항은! 2번째 파라메터에서 에러처리를 하면, promise 상태가 pending -> fulfilled로 변화하기 때문에 
    (참고: promise 상태가 pending -> rejected -> fulfilled 되는 것 X)
  */
  .then((response) => response.json(), (error) => { // 2번) 
    console.log(error);
    return error; // 3번) 여기서 리턴한 값이된다.
  })
  .then((result) => { console.log(result); }); // 2번) 실패해도 다음 then으로 넘어가고, 대신 넘어온 promise 객체가

  // 4번) 참고: 2번째 파라메터에서 에러처리를 하지않았는데, 1번째 파라메터에서 에러가 발생해야 -> rejected 상태!
  // .then((response) => response.json())
  // .then((result) => { console.log(result); });
  1. rejected 기본 예시 2 (with catch)
fetch('https://jsonplaceholder.typicode.com/users')
  .then((response) => response.json())
  .catch((error) =>  { console.log(error); }) // 1번) catch는 promise 객체가 rejected 상태가 되면 실행할 콜백함수를 등록하는 메서드.
  // .then(undefined, (error) => { console.log(error); }) // 2번) catch는 then 메서드의 1번째 인자에 undefined를 넣은 것과 같은 의미이다.
  .then((result) => { console.log(result); }); // 3번) 실패해도 다음 then으로 넘어가고, 대신 넘어온 promise 객체가, catch에서 리턴한 값이된다.

  // 4번) 그러나 보통, 어느 promise 객체가 rejected 상태가 되어도 잘 대응하기 위해서 -> catch 메서드는 맨 마지막에 작성한다. (실무에서 특히!)
  .then((response) => response.json())
  .then((result) => { console.log(result); }) // 5번) 대신, 어느 promise 객체에서 rejected 상태가 되었는지 판단하기 위해서, 이 안에서 추가 처리를 해준다. (예시에는 추가 처리를 해주지 않았음)
  .catch((error) =>  { console.log(error); }); // 6번) 그러나, 어떤 promise 객체가 rejected 된 후에도 작업을 살릴 방법이 있다면
  // .then((result) => { console.log(result); }); // 7번) catch 메서드를 사용한 후에도, then 메서드로 추가 작업을 해도 된다!
  1. finally 기본 예시
fetch('https://jsonplaceholder.typicode.com/users')
  .then((response) => response.json())
  .then((result) => { console.log(result); })
  .catch((error) =>  { console.log(error); })
  .finally(() => { console.log('END'); }); // 성공, 실패 여부와 상관없이 마지막에 작동한다 (finally 메서드는 보통, catch 메서드 뒤에 쓴다)
  1. fulfilled vs rejected 상황에 따른 작동 예시
  • fulfilled
fetch('https://jsonplaceholder.typicode.com/users') // 1번) fulfilled 상태가 되면
  .then((response) => response.json())
  .then((result) => { console.log(result); console.log('A'); }) // 2번) 동작
  .catch((error) => { console.log('B'); throw new Error('test'); })
  .then((result) => { console.log(result); console.log('C'); }) // 3번) 동작 
  .then(undefined, (error) => { console.log('D'); })
  .catch((error) => { console.log('E'); })
  .then((result) => { console.log(result); console.log('F'); }) // 4번) 동작
  .finally(() => { console.log('final'); }); // 5번) 동작
  • rejected
fetch('https://www.error.www') // 1번) rejected 상태가 되면
  .then((response) => response.json())
  .then((result) => { console.log(result); console.log('A'); })
  .catch((error) => { console.log('B'); throw new Error('test'); }) // 2번) 동작
  .then((result) => { console.log(result); console.log('C'); })
  .then(undefined, (error) => { console.log('D'); }) // 3번) 동작 // 6번) rejected를 처리한 후에, 추가적으로 error를 던지지 않으면
  .catch((error) => { console.log('E'); }) // 7번) 그 뒤에 연속적으로 연결되어있는 catch 구문은 실행되지 않는다.
  .then((result) => { console.log(result); console.log('F'); }) // 4번) 동작
  .finally(() => { console.log('final'); }); // 5번) 동작

new Promise

  1. new Promise 란?
    - promise 객체를 직접 생성할 수 있다.
    - resolve: 생성될 promise 객체를 fulfilled 상태로 만들 수 있는 함수가 연결됩니다.
    - reject: 생성될 promise 객체를 rejected 상태로 만들 수 있는 함수가 연결됩니다.

  2. then catch finally 메서드 기본 예시 1

  • fulfilled
const p = new Promise((resolve, reject) => {
  setTimeout(() => { resolve('success'); }, 2000); // 1번) 2초후에 promise 객체가 fulfilled 상태가 되고, resolve(괄호)안에 값이
});

p.then((result) => { console.log(result); }); // 2번) 리턴값으로 넘어오게 된다. (result 값으로 넘어오는 것)
  • reject
const p = new Promise((resolve, reject) => {
  setTimeout(() => { reject('fail'); }, 2000); // 1번) 2초후에 promise 객체가 rejected 상태가 되고, reject(괄호)안에 값이
});

p.catch((error) => { console.log(error); }); // 2번) 리턴값으로 넘어오게 된다. (error 값으로 넘어오는 것)
  1. then catch finally 메서드 기본 예시 2
function starbucks(coffename) {
  const p = new Promise((resolve, reject) => {
    if (coffename === "아메리카노") {
      resolve("아메리카노 한잔입니다.");
    } else {
      reject("해당메뉴는 없습니다.");
    }
  });

  return p;
};

starbucks("아메리카노") // 아메리카노 -> then + finally // 카페라떼 -> catch + finally
  .then((resolve) => console.log(resolve)) // 아메리카노 한잔입니다. 감사합니다.
  .catch((reject) => console.log(reject)) // 해당메뉴는 없습니다. 감사합니다.
  .finally(() => console.log("감사합니다."));
  1. 상태가 결정된 Promise 객체 만들기
// fulfilled 상태의 Promise 객체
const successPromise = Promise.resolve('success');
successPromise.then((result) => { console.log(result); }, (error) => { console.log(error); });

// rejected 상태의 Promise 객체
const failPromise = Promise.reject(new Error('fail'));
failPromise.then((result) => { console.log(result); }, (error) => { console.log(error); });

all 메서드

  1. all 메서드 란?
    - all 메서드도 then 메서드처럼 새로운 Promise 객체를 리턴합니다.
    - 아규먼트로 들어온 배열 안에 있는 모든 Promise 객체가 pending 상태에서 fulfilled 상태가 될 때까지 기다립니다.

  2. 모든 promise 객체가 fulfilled 상태일 경우
    - 모든 Promise 객체들이 fulfilled 상태가 되면, all 메서드가 리턴했던 Promise 객체는 fulfilled 상태가 됩니다.
    - 그리고 각 Promise 객체의 작업 성공 결과들로 이루어진 배열을 all의 작업 성공 결과로 갖게 됩니다.

  3. 객체들 중 하나라도 rejected 상태가 되는 경우
    - all 메서드는 하나의 Promise 객체라도 rejected 상태가 되면, 전체 작업이 실패한 것으로 간주합니다.
    - 그리고 all의 promise 객체는 rejected 상태가 되고, 동일한 작업 실패 정보를 갖게됩니다.

  4. 코드 예시

// 1번 직원 정보
const p1 = fetch('https://learn.codeit.kr/api/members/1').then((res) => res.json());
// 2번 직원 정보
const p2 = fetch('https://learn.codeit.kr/api/members/2').then((res) => res.json());
// 3번 직원 정보
const p3 = fetch('https://learn.codeit.kr/api/members/3').then((res) => res.json());

Promise
  .all([p1, p2, p3])
  .then((results) => {
    console.log(results); // [1번 직원 정보, 2번 직원 정보, 3번 직원 정보]
  })
  .catch((error) => {
    console.log(error);
  });

race 메서드

  1. race 메서드 란?
    - race 메서드도 all 메서드와 마찬가지로 여러 Promise 객체들이 있는 배열을 아규먼트로 받습니다.
    - race 메서드가 리턴한 Promise 객체는 아규먼트로 들어온 배열의 여러 Promise 객체들 중에서, 가장 먼저 fulfilled 상태 또는 rejected 상태가 된 Promise 객체와 동일한 상태와 결과를 갖습니다.
    - 말그대로 race 메서드는 여러 Promise 객체들을 레이스(race, 경쟁)시켜서 가장 빨리 상태가 결정된 Promise 객체를 선택하는 메서드입니다.

  2. 코드 예시

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Success'), 5000);
});
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => reject(new Error('fail')), 2000); // 1번) 가장먼저 rejected 상태가 되기 때문에
});
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => reject(new Error('fail2')), 4000);
});

Promise
  .race([p1, p2, p3])
  .then((result) => {
    console.log('성공');
    console.log(result);
  })
  .catch((value) => { // 2번) catch가 실행됩니다.
    console.log('실패');
    console.log(value);
  });

allSettled 메서드

  1. allSettled 메서드 란?
    - 배열 내의 모든 Promise 객체가 fulfilled 또는 rejected 상태가 되기까지 기다리고, pending 상태의 Promise 객체가 하나도 없게 되면 A의 상태값은 fulfilled 상태가 되고 그 작업 성공 결과로 하나의 배열을 갖게 됩니다.
    - 이 배열에는 각 promise 객체의 (1) 최종 상태는 status 프로퍼티, (2) 그 작업 성공 결과는 value 프로퍼티, (3) 그 작업 실패 정보는 reason 프로퍼티가 객체요소로 담겨있습니다.

  2. 코드 예시

[
   {status: "fulfilled", value: 1},
   {status: "fulfilled", value: 2},
   {status: "fulfilled", value: 3},
   {status: "rejected",  reason: Error: an error}
]

profile
JS, TS, React, Vue, Node.js, Express, SQL 공부한 내용을 기록하는 장소입니다

0개의 댓글