📌 프로미스(Promise)란?

Promise 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냄

  • 비유로 이해하기

    • 가정 상황

      • 상점에서 물건 주문 시, 나올 때 까지 시간이 걸림.
      • 언제 완료 될지 모름 & 가끔 물건 제작 실패하는 경우 有
    • 소비자의 대안

      1. 10초에 한 번씩 묻기 (❌)

        다 됐나요? 아니요. 다 됐나요? 아니요. 다 됐나요? 네 준비 되었습니다. 가지고 가세요.

        가끔 다 됐나요? 물으면 만들다가 실패한 경우도 있을 것. → 그럼 다시 물건을 주문해야 함.

      2. 전화 번호 주고 나오기 (⭕)

        상점에 주문 후, (상품이 준비되거나 실패하면 알려달라고) 전화번호를 주고 나오는 게 좋음.

        • 소비자

          준비되는 동안 다른 작업 가능

        • 상점

          번호 기억했다가 상품 준비되거나 실패 했을 때 소비자에게 알림.

        → 이럴 때 사용할 수 있는 것이 Promise

📌 사용법 & 원리

🔹 사용법

const pr = new Promise((resolve, reject)=>{
  // code
});
  • new Promise 로 생성.

  • 함수를 전달 받음

    • 인수
      • resolve

        성공 시, 실행되는 함수

      • reject
        실패 시, 실행되는 함수

        ⇒ 콜백함수 (이렇게 어떤 일이 완료된 후 실행되는 함수)

🔹 원리

  • new Promise 생성자가 반환하는 Promise 객체의 프로퍼티

    state , result

  • 성공할 경우

    • state

      pending(대기) 였다가 resolve가 호출되면(==성공하면) fulfilled가 됨.

    • result

      resolve함수로 전달된 값

  • 실패할 경우

    • state

      pending(대기) 였다가 reject가 호출되면(==실패하면) rejected가 됨.

    • result

      reject함수로 전달된 error


🔹 판매자

주문을 받으면 3초 동안 뭔가를 하고 성공/실패 알림.

  • 3초 걸리도록 setTimeout 이용

  • 성공할 경우

    • state

      pending이였다가 3초 후 fulfilled로

    • result

      undefined였다가 ‘OK’

  • 실패할 경우

    • state

      pending이였다가 3초 후 rejected로

    • result

      undefined였다가 error

🔹 소비자

◽ then

이용해서 resolve와 reject 처리 가능

  • 첫 번째 인수

    promise가 이행되었을 때, 실행하는 함수

    ex. 예시의 result에 들어오는 값: ‘OK’

  • 두 번째 인수

    거부되었을 때 실행하는 함수

    ex. 예시의 err에 들어오는 값: error 값

  • 예시

    지금 resolve로 실행됨 → 이 상황에서 두 번째 콜백은 실행 안됨.

◽ catch

reject 인(에러 발생) 경우에만 실행

  • 사용법

    두 번째로 전달했던 함수를 catch 내부에 기입 (동일하게 동작)

  • 장점

    • 가독성 더 좋음 (catch로 명확히 구분)

    • 첫 번째 함수의 에러도 잡기 가능

    → catch문 사용이 훨씬 좋음

  • 예시

    왼쪽의 코드는 오른쪽의 코드로 교체 가능.

◽ finally

처리가 완료되면 항상 실행 (이행 / 거부 상관 無 )

  • 유용한 경우

    로딩 화면 같은 것을 없앨 때

  • 예시

    pr.then(
    	function(result){}
    ).catch(
    	function(err){}
    ).finally(
    	function(){
        console.log('--- 주문 끝 ---') // 이것 처럼 사용 가능
      }
    )

◽ 예시

  • resolve 일 경우

    const pr = new Promise((resolve, reject)=> {
      setTimeout(()=>{
        resolve('OK')
      },1000)
    })
    
    console.log("시작")
    pr.then((result)=>{
      console.log(result)
    })
      .catch((err)=>{
        console.log(err)
      })
      .finally(()=>{
        console.log('끝')
      })
    
    /*
    - 콘솔:
    '시작'
    'OK'
    '끝'
    
    - 설명:
    시작이 나오고 1초 후에 OK가 나옴. */
  • reject 일 경우

    const pr = new Promise((resolve, reject)=> {
      setTimeout(()=>{
        reject(new Error('err..')) // 👈 reject로 바꿔서 작성 하면
      }, 1000)
    })
    
    (...생략)
    
    /*
    - 콘솔:
    '시작'
    Error: 'err..'
    '끝'
    
    - 설명:
    1초 있다가 에러 뜸. */
  • finally 의 ‘끝’ log → 항상 뜸. (이행 / 거부 무관)

📌 콜백 지옥 (Callback Hell)

depth가 깊어지면서 계속 Callback을 호출하는 것

  • 예시 ( Promise 활용 X )

    총 3개의 상품 주문

    • 코드
      // 1번 주문
      const f1 = (callback)=>{
        setTimeout(function(){
          console.log('1번 주문 완료');
          callback();
        },1000)
      };
      
      // 2번 주문
      const f2 = (callback)=>{
        setTimeout(function(){
          console.log('2번 주문 완료');
          callback();
        },3000)
      }
      
      // 3번 주문
      const f3 = (callback)=>{
        setTimeout(function(){
          console.log('3번 주문 완료');
          callback();
        },2000)
      }
      
      // 👇 [사용] 콜백 지옥
      console.log('시작');
      f1(function(){
        f2(function(){
          f3(function(){
            console.log('끝')
          })
        })
      })
    • 해설 (사용하는 부분)
      1. 처음 f1을 실행하는데 callback에서 함수를 받아야 함.

        그런데 1번 주문이 끝나면 2번 주문을 해야 함. → 함수 내부에 f2가 들어가게 됨.

      2. f2도 마찬가지로 callback 함수를 받음

        → 내부에 함수가 있어야 함.

        → f2가 끝나면 f3를 실행해야 함

      3. f3도 마찬가지로 callback 함수를 받음

        → 함수가 들어감

      4. 끝남

        → ‘끝’이라고 적어 줌.

    • 테스트 결과

      잘 동작함.

      '시작'
      '1번 주문 완료'
      '2번 주문 완료'
      '3번 주문 완료'
      '끝'

📌 프로미스 체이닝(Promises chaining)

Promise가 계속 연결 되는 것

🔹 콜백 지옥 예시 문제

◽ 해결

  • Promise 활용
  • callback 함수 활용 X
  • 동작들: 동일
  • 코드
    
    const f1 = () => {
      return new Promise((res, rej) => {
        setTimeout(()=>{
          res('1번 주문 완료')
        }, 1000)
      })
    };
    
    const f2 = (message) => {
      console.log(message)
      return new Promise((res, rej) => {
        setTimeout(()=>{
          res('2번 주문 완료')
        }, 3000)
      })
    }
    
    const f3 = (message) => {
      console.log(message)
      return new Promise((res, rej) => {
        setTimeout(()=>{
          res('3번 주문 완료')
        }, 2000)
      })
    }
    
    // 👇 [사용]
    console.log('시작')
    f1()
    	.then(res => f2(res))
    	.then(res => f3(res))
    	.then(res => console.log(res))
    	.catch(console.log)
    	.finally(()=>{
      	console.log('끝')
    	})
  • 해설 (사용)
    1. f1을 실행하고 f1은 Promise 반환

      resolve함수를 실행하면서 넘겨준 값 → 2번에게 넘김.

    2. f2는 message를 받아서 log를 찍고 마찬가지로 promise를 반환

      resolve함수로 전달한 값을 3번에서 사용 가능

    3. 3번도 message를 받아서 찍고 promise 반환

      → then 사용 가능

    4. 마지막으로 넘겨받은 message → console 찍음.

    5. 에러 처리 & finally 함.

  • 테스트 결과
    시작
    1번 주문 완료
    2번 주문 완료
    3번 주문 완료
    끝

◽ 실패일 경우

2번 → 실패(reject)로 수정

  • 코드
    (...생략)
    
    const f2 = (message) => {
      console.log(message)
      return new Promise((res, rej)=>{
        setTimeout(()=>{
          rej('xxx') // 👈 수정
        }, 3000)
      })
    }
    
    (...생략)
  • 테스트 결과
    시작
    1번 주문 완료
    xxx
    끝
  • 해설
    1. 1번 성공
    2. 2번 실패
    3. finally 실행
    • 3번 : 시도조차 안함.

📌 Promise.all

  • 순회 가능한 객체에 주어진 모든 프로미스가 이행한 후, 혹은 프로미스가 주어지지 않았을 때 이행하는 Promise를 반환.
  • 주어진 프로미스 중 하나가 거부하는 경우, 첫 번째로 거절한 프로미스의 이유를 사용해 자신도 거부.

🔹 사용하는 경우

  • 세 상품들

    • 순차적으로 주문 시, 걸리는 시간: 총 6초

    • 동시 주문 시, 걸리는 시간: 3초 (제일 오래 걸리는 상품의 시간)

      → 이 때 사용 하는 것: Promise.all

🔹 사용법

Promise.all(iterable);
// iterable: Array와 같이 순회 가능한(iterable) 객체.

🔹 동작 원리

(...생략(프로미스 체이닝 - 콜백 지옥 예시 문제 - 해결 - 코드))

Promise.all([f1(), f2(), f3()]).then(res => {
  console.log(res)
})

/* 
- 결과:
undefined (message가 전달된 것이 없어서 나옴)
undefined (              "                )
[ '1번 주문 완료', '2번 주문 완료', '3번 주문 완료' ] 👈 */
  1. Promise.all 쓰고 배열로 받음.

  2. 배열 내 작업이 완료 되어야 then 부분 실행

  3. 결과

    Promise 에 넘겨준 값이 배열로 들어옴.

🔹 시간 절약 가능

이유: 한꺼번에 시작 후, 모두 이행되면 값 사용 가능하기 때문

  • 측정 방법

    console.time 활용

  • 측정 결과

    • Promise.all
      console.time('x')
      Promise.all([f1(), f2(), f3()]).then(res => {
        console.log(res)
        console.timeEnd('x')
      })
      
      /*
      - 결과:
      (...생략)
      'x: 3014ms' 👈 약 3초 걸림 */
    • Promise (이전 코드)
      console.time('시작')
      f1()
      	.then(res => f2(res))
      	.then(res => f3(res))
      	.then(res => console.log(res))
      	.catch(console.log)
      	.finally(()=>{
        	console.timeEnd('시작')
      	})
      
      /*
      - 결과:
      (... 생략)
      시작: 6024ms 👈 약 6초 걸림 */

🔹 주의 (reject 될 경우)

실패했다고 뜨고 어떤 데이터도 얻지 못함.

const f1 = () => {
  return new Promise((res, rej)=>{
    setTimeout(()=>{
      rej(new Error('xx')) // 👈 수정
    }, 1000)
  })
};

(...생략)

// 결과: Uncaught (in promise) Error: xx
  • Promise : 가져간 데이터 순으로 화면에 그리기 가능
  • Promise.all : “ 불가

🔹 사용하는 경우

다 보여주거나 아예 안보여주거나 할 때 사용

(하나의 정보라도 누락되면 페이지를 안 보여줘야 되는 경우)

📌 Promise.race

iterable 안에 있는 프로미스 중에 가장 먼저 완료된 것의 결과값으로 그대로 이행하거나 거부하는 Promise 객체를 반환

🔹 사용 방법

Promise.all 과 동일. (all → race 대체)

Promise.race(iterable);
// iterable: Array와 같은 iterable 객체.

🔹 Promise.all 과 차이점

  • all: 모든 작업이 완료 될 때까지 기다림
  • race: 하나라도 1등으로 완료되면 끝냄.

🔹 예시

const f1 = () => {
  return new Promise((res, rej)=>{
    setTimeout(()=>{
      res('1번 주문 완료')
    }, 1000)
  })
};

const f2 = (message) => {
  console.log(message)
  return new Promise((res, rej)=>{
    setTimeout(()=>{
      rej(new Error('xx'))
    }, 3000)
  })
}

const f3 = (message) => {
  console.log(message)
  return new Promise((res, rej)=>{
    setTimeout(()=>{
      res('3번 주문 완료')
    }, 2000)
  })
}

console.time('x')
Promise.race([f1(), f2(), f3()]).then(res => { // 👈 race 사용
  console.log(res)
  console.timeEnd('x')
})
  • 결과

    undefined
    undefined
    1번 주문 완료 👈
    x: 1003ms 👈
  • 해설

    1번 주문 완료가 나왔고 2번이 reject를 예정하고 있었지만, 이미 1번이 완료되어서 무시됨.

🔹 사용 하는 경우

용량이 큰 이미지를 로딩하는데 그 중에 하나라도 완료되면 그 이미지를 보여줄 때


참고

profile
복습 목적 블로그 입니다.

0개의 댓글