Promise

📄 promise

프로미스는 주로 서버에서 데이터를 받아올 때 사용합니다.

서버에 요청을 보내고 데이터를 받아올 때, 아직 데이터를 다 받아오기도 전에 데이터를 다 받아온것 처럼 화면에 데이터를 표시하고 오류가 발생합니다.

이런 문제점을 해결하는 방법이 프로미스 입니다

프로미스는 ES6부터 JavaScript의 표준 내장 객체로 추가되었습니다.

📄 promise 처리 흐름

1. 프로미스가 생성자를 통해서 생성되면 pending(대기)상태가 됩니다.

new Promise((resolve, reject) => {}); //pending

2-1. pending상태로 돌입한 후 excutor 함수 인자 중 하나인 resolve 함수를 실행하면, fulfilled(이행)상태가 됩니다.

new Promise((resolve, reject) => {
  // pending상태
  // ... 비동기적인 상황이 되는 처리가 벌어짐.
  resolve(); // fulfilled
});

2-2. excutor 함수 인자 중 하나인 reject 함수를 실행하면, rejected (거부) 상태가 됩니다.

new Promise((resolve, reject) => {
  reject(); //rejected
});

3-1. 프로미스 객체가 fulfilled 되는 시점에 .then 안에 설정한 callback 함수가 실행됩니다.

p.then(/*callback*/);

p라는 프로미스 객체의 callback이 실행됨.

excutor의 resolve함수를 실행할 때 인자를 넣어 실행하면, then의 callback 함수의 인자로 받을 수 있습니다.

resolve('hello');
then((message) => {...})

3-2. 마찬가지로 프로미스 객체가 rejected되는 시점에 .catch안에 설정한 callback함수가 실행됩니다.

catch(() => {
    console.log('/*callback*/');
});

executor의 reject 함수를 실행할 때 인자를 넣어 실행하면, catch의 callback함수의 인자로 받을 수 있습니다.

reject('error');
catch((reason) => {...})

보통 reject 함수를 실행하며 rejected 되는 이유를 넘기는데, 표준 내장 객체인 Error의 생성자를 이용하여 Error 객체를 만들 수 있습니다.

reject(new Error('bad'));
catch((error) => {...});

4. fulfilled 되거나 rejected 된 후에 최종적으로 실행할 것이 있다면, .finally()를 설정하고, 함수를 인자로 넣습니다.

finally(() => {...});

📄 callback vs promise

비동기 작업을 하는 방법은 주로 callback과 promise가 있습니다.

1. callback

보통 비동기 작업을 할 때, callback함수를 인자로 넣어 로직이 끝나면 callback함수를 호출합니다.

이런 경우 함수가 아래로 진행되지 않고, callback 함수 안으로 진행됩니다.

function c(callback) {
  setTimeout(() => {
    callback();
  }, 1000);
}

c(() => {
  console.log("1000ms 후에 callback 함수가 실행됩니다.");
});

c(() => {
  c(() => {
    c(() => {
      console.log("3000ms 후에 callback 함수가 실행됩니다");
    });
  });
}); //callback hell (콜백 지옥)

2. promise 체이닝

promise방식에서는 then 함수에서 다시 프로미스 객체를 리턴하는 방법을 통해 체이닝하면, 비동기 적업을 순차적으로 아래로 표현할 수 있습니다.

function p() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve();
    }, 1000);
  });
}

p()
  .then(() => {
    return p(); // 다시 새로운 프로미스 객체를 만들어서 리턴한다.
  })
  .then(() => p())
  .then(p)
  .then(() => {
    console.log("4000ms 후에 fulfilled 됩니다.");
  });

📄 value가 프로미스 객체인지 알 수 없는 경우

연결된 then 메서드를 실행합니다.

value가 프로미스 객체면, resolve 된 then 메서드를 실행합니다.

value가 프로미스 객체가 아니면, value를 인자로 보내면서 then메서드를 실행합니다.

Promise.resolve(/* value */); //프로미스라는 전역객체의 resolve함수를 실행하면서 동시에 promise를 만들어낸다.

Promise.resolve(
  new Promise((resolve, reject) => {
    // 비동기 프로미스 객체를 생성해서 resolve의 인자인 value값으로 설정.
    setTimeout(() => {
      resolve();
    }, 1000);
  })
).then((data) => {
  console.log(
    "프로미스 객체인 경우, resolve 된 결과를 받아 then이 실행됩니다.",
    data
  );
});

Promise.resolve("bar").then((data) => {
  console.log("then 메서드가 없는 경우, fulfilled됩니다.", data);
});

promise.reject를 사용하면, catch로 연결된 rejected 상태로 변경됩니다.

Promise.reject(/* */);

Promise.reject(new Error("reason"))
  .then((error) => {})
  .catch((error) => {
    console.log(error);
  });

📄 promise 객체를 배열로 생성하기

promise.all

프로미스 객체를 여러개를 생성하여,

배열로 만들어 인자로 넣고 Promise.all을 실행하면,

배열의 모든 프로미스 객체들이 fulfilled 되었을 때, then의 함수가 실행됩니다.

then의 함수의 인자로 프로미스 객체들의 resolve 인자값을 배열로 돌려줍니다.

Promise.all([프로미스 객체들]);

promise.all에 전달되는 프라미스 중 하나라도 거부되면, Promise.all이 반환하는 프라미스는 에러와 함께 바로 거부됩니다.

promise.allSettled

Promise.allSettled는 모든 프라미스가 처리될 때까지 기다립니다.

반환되는 배열은 다음과 같은 요소를 갖습니다.

  • 응답이 성공할 경우 – {status:”fulfilled”, value:result}
  • 에러가 발생한 경우 – {status:”rejected”, reason:error}
let urls = [
  "https://api.github.com/users/iliakan",
  "https://api.github.com/users/Violet-Bora-Lee",
  "https://no-such-url",
];

Promise.allSettled(urls.map((url) => fetch(url))).then((results) => {
  results.forEach((result, num) => {
    if (result.status == "fulfilled") {
      alert(`${urls[num]}: ${result.value.status}`);
    }
    if (result.status == "rejected") {
      alert(`${urls[num]}: ${result.reason}`);
    }
  });
});

결과

[
  {status: 'fulfilled', value: ...응답...},
  {status: 'fulfilled', value: ...응답...},
  {status: 'rejected', reason: ...에러 객체...}
]

promise.race

프로미스 객체 여러개를 생성하여,

배열로 만들어 인자로 넣고 Promise.race를 실행하면,

배열의 모든 프로미스 객체들 중 가장 먼저 fulfiiled된 것으로, then의 함수가 실행됩니다.

then의 함수의 인자로 가장 먼저 fulfilled 된 프로미스 개체의 resolve 인자값을 돌려줍니다.

Promise.race([프로미스 객체들]);

출처

Leave a comment