• 230215 TIL : 비동기 프로그래밍 (Promise Methods)

    2023. 2. 16.

    by. 옛슬

    오늘은 어제에 이어서 Promise의 메서드에 대해서 공부해 보려고 한다.

    그 이유는 MDN에서 나는 주로 공부를 하는데,

    알고보니 프로미스에도 다양한 메서드가 있는것이다

    물론 이걸 다 외울 순 없겠지만 이해는 하고 있으면 좋을거 같아서 정리해보려고 한다.

     


    Promise.all()

    - 순회 가능한(배열과 같은) 프로미스를 받아서 하나의 프로미스로 리턴한다.

    - 리턴 값은  1️⃣ 모든 프로미스가 fulfilled 했을 때 fulfilled 값을 리턴하고,

      혹은 2️⃣ 하나의 값이라도 rejected 되었으면 첫번 째 reject값을 반환한다.

    - 해당 메서드는 프로미스의 결과를 집계할 때 유용하다.

     

    예시 )

    - 예시를 어떤거를 해볼까라고 생각했는데 갑자기 취업 면접이 생각났다 여튼 중간에 탈락하면 바로 끝이니 ㅠ

        function doJobInterview(interviewStep) {
          return new Promise((resolve, reject) => {
            const interviewScore = Math.floor(Math.random() * 100);
            const isPassed = interviewScore > 40;
    
            if (isPassed) {
              resolve(`축하드립니다, ${interviewStep}을 합격하셨습니다! 자세한 내용은 홈페이지를 참고해주세요.`);
            } else {
              reject(
                `당사의 사원모집에 관련한 면접에 참석하여 주심을 감사 드립니다. 
              귀하의 인상적인 경력 및 면접에도 불구하고 예상보다 많은 지원자들로 인해 ${interviewStep}을 
              합격하지 못하였음을 통보 드립니다. 
              아쉽지만 귀하의 면접 참석에 대해 다시 한번 감사 드리며 
              앞으로의 귀하의 취업에 좋은 결과 있기를 기원하겠습니다.`
              )
            }
          });
        }
    
        Promise
          .all(['과제면접', '직무면접', '임원면접'].map(doJobInterview))
          .then(message => console.log(message))
          .catch(message => console.error(message));

    - 모두 fulfilled 시 모든 값의 fulfilled 값 리턴

    - 하나라도 틀리면 틀린 첫번째 값 리턴

    - 나름 현실감 있게 만들어보았다 

     

    Promise.allSettled()

    - 성공 여부 보다는 여러 비동기 작업을 수행해야 하거나, 항상 각 프로미스의 실행 결과를 알고 싶을 때 사용

    - 반환 값은 프로미스에 대한 결과 값을 나타내는 객체 배열을 리턴.

        function getAnonymouseFeedback(drink) {
          return new Promise((resolve, reject) => {
            const score = Math.floor(Math.random() * 100);
            const passedScore = score > 60;
            const evaluation = passedScore ? '맛있어요' : '맛없어요';
            const evaluationTemplate = `음료 점수 : ${score}점 / 평가 : ${evaluation}`
    
            if (passedScore) {
              resolve(evaluationTemplate);
            } else {
              reject(evaluationTemplate)
            }
          });
        }
    
        Promise
          .allSettled(['아메리카노', '카페라떼', '초코라떼'].map(getAnonymouseFeedback))
          .then(message => console.log(message))

    - 반환된 객체에서 status를 확인할 수 있고, fulfilled시 value를 볼 수 있으며, rejected되면 reason을 볼 수 있다.

    - 이를 통해, 각각의 프로미스들이 성공했는지 실패했는지 확인할 수 있다. 그리고 이렇게 나눌 수도 있다!

        function getAnonymouseFeedback(drink) {
          return new Promise((resolve, reject) => {
            const score = Math.floor(Math.random() * 100);
            const passedScore = score > 60;
            const evaluation = passedScore ? '⭐⭐⭐⭐⭐' : '⭐⭐';
            const evaluationTemplate = `음료 점수 : ${score}점 / 평가 : ${evaluation}`
    
            if (passedScore) {
              resolve(evaluationTemplate);
            } else {
              reject(evaluationTemplate)
            }
          });
        }
    
        Promise
          .allSettled(['아메리카노', '카페라떼', '초코라떼'].map(getAnonymouseFeedback))
          .then(results => {
            return {
              good: results.filter(result => result.status === 'fulfilled'),
              bad: results.filter(result => result.status === 'rejected')
            }
          })
          .then(results => {
            console.log('~ 피드백 리스트 ~')
            results.good.forEach(goodFeedback => console.log(`정말 대단한 바리스타군요! 🥰 from Kim / ${goodFeedback.value}`))
            results.bad.forEach(badFeedback => console.log(`죄송하지만, 음료 만드는 재능은 없으신거 같습니다 😥 from Kim / ${badFeedback.reason}`))
          })

     

    Promise.any()

    - 어떤 값 하나라도 성공 시, 첫번 째 fulfilled 값을 반환

    - 만약에 모두 실패 시, 모든 rejected 값을 반환

    - 사실 요거는 언제 사용하면 좋을까? 라는 생각이 들었는데, MDN에서 유용한 예시가 있어서 가지고 왔다.

      바로 첫번째 이미지를 로드하는 함수이다.

    async function fetchAndDecode(url, description) {
      const res = await fetch(url);
      if (!res.ok) {
        throw new Error(`HTTP error! status: ${res.status}`);
      }
      const data = await res.blob();
      return [data, description];
    }
    
    const coffee = fetchAndDecode("coffee.jpg", "Coffee");
    const tea = fetchAndDecode("tea.jpg", "Tea");
    
    Promise.any([coffee, tea])
      .then(([blob, description]) => {
        const objectURL = URL.createObjectURL(blob);
        const image = document.createElement("img");
        image.src = objectURL;
        image.alt = description;
        document.body.appendChild(image);
      })
      .catch((e) => {
        console.error(e);
      });

    - 둘 중 아무나 먼저 fetching되면 해당 이미지가 보이는 것

    - 즉, 해당 메서드는 어떤 값이 먼저 오든 하나라도 성공하는 것이 목적이라면 사용하는게 좋은거 같다. 왜냐면 첫번째     

       fulfilled 값을 반환하기 때문에 다음 값을 기다리진 않기 때문이다.

    - 만약에 모든 값이 실패하고 reject 값을 반환하면 AggregateError를 반환한다.

     

    AggregateError

    - 에러를 나타내는 객체이며 여러 에러를 하나의 에러로 묶을 때 사용한다.

    - 저렇게 나오고 AggregateError는 4개의 속성을 가지고 있다.

    - AggregateError.prototype.message  →  ' All Promises rejected'

    - AggregateError.prototype.name  → 'AggregateError'

    - AggregateError.prototype.cause  → undefined

    - AggregateError.prototype.errors  →  [ Error: "some error" ]

     

    Promise.race()

    - 메서드의 이름처럼 레이스 후 가장 처음으로 리턴되는 값을 반환한다.

    - 이 값은 성공 일수도 , 실패일 수도 있으며 then()과 catch()를 통해 프로미스의 값이 반환된다.

        function race(name, ms) {
          return new Promise((resolve) => {
            const seconds = ms / 1000;
            
            setTimeout(function () {
              resolve(`1등은 바로 ${name}님! 100m 달리기 결과 : ${seconds}초`)
            }, ms)
          })
        }
    
        Promise.race([race('토끼', 5000), race('거북이', 10000)]).then(value => console.log(value));
        
        // 1등은 바로 토끼님! 100m 달리기 결과 : 5초

    - 실무에서는 이런 방식으로도 사용할 수 있겠다 ( MDN 참고 )

    const data = Promise.race([
      fetch("/api"),
      new Promise((resolve, reject) => {
        // Reject after 5 seconds
        setTimeout(() => reject(new Error("Request timed out")), 5000);
      }),
    ])
      .then((res) => res.json())
      .catch((err) => displayError(err));

    - api를 5초동안 기다려주고 그뒤에는 바로 reject 날려서 더이상 기다리지 않도록 하는 것. 물론 뭔가 실제로 쓰지는 못할거 같지만?

     


    오늘은 간단하게 Promise의 메서드들을 살펴보았다.

    생각보다 엄청 많아서 당황... 사실 뭔가 나는 큰 틀만 공부하고 넘어간 부분이 꽤 많아서 그랬는지는 

    몰라도 새로운 메서드들을 많이 배웠다. 이거를 실무에서는 어떻게 사용하는지 알아보는게 좋을거 같다.

     

    참고자료

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/any

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError

    댓글