📌 프로미스(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

이용해서 resolvereject 처리 가능

  • 첫 번째 인수

    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을 실행하고 f1Promise 반환

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

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

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

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

      then 사용 가능

    4. 마지막으로 넘겨받은 messageconsole 찍음.

    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 과 동일. (allrace 대체)

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개의 댓글