[JS] Promise

zzincode·2024년 6월 23일
0

JavaScript

목록 보기
17/24
post-thumbnail

Promise

자바스크립트는 비동기 처리를 위한 하나의 패턴으로 콜백함수를 사용
하지만 콜백패턴은 콜벡헬로 인해 가독성이 나쁘고 처리 중 발생한 에러의 처리가 곤란하며 여러개의 비동기 처리를 한번에 처리하는데 한계가 있어 ES6에서는 비동기처리를 위한 또 다른 패턴으로 promise 도입

promise 생성자 함수는 비동기 처리를 수행할 콜백함수를 인수로 전달받는데 이 콜백함수는 resolve와 reject함수를 인수로 전달받는다.

    const promise = new Promise((resolve, reject) => {
    	if(/*비동기 처리 성공*/){
    		resolve('result');
    	} else { /*비동기 처리 실패*/
    		reject('failure reason');
    	}
    });
    
    const promiseGet = url => {
      return new Promise((resolve, reject) => {
          const xhr = new XMLHttpRequest();  
          xhr.open('GET', url);             
          xhr.send();
    
          xhr.onload = () => {
              if (xhr.status === 200) {
                  resolve(JSON.parse(xhr.response));
              } else {
                  reject(new Error(xhr.status)); 
              }
          };
      });
    };
    
    promiseGet('https://jsonplaceholder.typicode.com/posts/1');
 //promiseGet함수는 프로미스 반환

Promise 상태

pending : 비동기 처리가 아직 수행되지 않은 상태 - 프로미스가 생성된 직후 기본 상태

fulfilled : 비동기 처리가 수행된 상태(성공)

→ resolve 함수 호출해 프로미스를 fulfilled 상태로 변경

rejected : 비동기 처리가 수해된 상태(실패)

→ reject 함수 호출해 프로미스를 rejected 상태로 변경

(fulfilled 또는 rejected 상태를 settled 상태라고 함)


프로미스의 후속 처리 메서드

Promise.prototype.then

두개의 콜백함수 인수로 전달

  • 프로미스가 fulfilled 상태가 되면 호출 - 비동기처리 성공 처리 콜백함수
  • 프로미스가 rejected 상태가 되면 호출 - 비동기 처리 실패 처리 콜백함수

Promise.prototype.catch

한개의 콜백함수 인수로 전달

  • 프로미스가 rejected상태인 경우만 호출

Promise.prototype.finally

  • 성공 실패 상관없이 무조건 한번만 호출
const promiseGet = url => {
  return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();  
      xhr.open('GET', url);              
      xhr.send();

      xhr.onload = () => {
          if (xhr.status === 200) {
              resolve(JSON.parse(xhr.response));
          } else {
              reject(new Error(xhr.status));
          }
      };
  });
};

promiseGet('https://jsonplaceholder.typicode.com/posts/1')
.then(res => console.log(res))
.catch(err => console.log(err))
.finally(()=> console.log('Bye'));

프로미스 에러처리

비동기처리에서 발생한 에러는

  • then 메서드의 두 번째 콜백함수에서도 처리가능
  • catch 매서드에서 처리가능

하지만! then 메서드의 두번째 콜백함수는 첫번째 콜백함수에서 발생한 에러를 캐치하지 못하고 코드가 복잡해져서 가독성 좋지 않음

따라서 에러처리는 then 메서드보단 catch 매서드에서 하는 것 권장

const promiseGet = url => {
  return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();  
      xhr.open('GET', url);              
      xhr.send();

      xhr.onload = () => {
          if (xhr.status === 200) {
              resolve(JSON.parse(xhr.response));
          } else {
              reject(new Error(xhr.status));  
          }
      };
  });
};

const url = 'https://jsonplaceholder.typicode.com';
promiseGet(`${url}/posts/1`)
 .then(({userId}) => promiseGet(`${url}/users/${userId}`))
 .then(userInfo => console.log(userInfo))
 .catch(err => console.error(err));

+) 프로미스도 콜백 패턴을 사용하므로 콜백함수를 사용하지 않는 것은 아님 → 가독성 떨어짐
⇒ async/await를 통해 해결 가능(후속처리 메서드 없이 동기처리처럼 프로미스가 처리 결과를 반환하도록 구현 가능)

(async () => {
  const {userId} = await promiseGet(`${url}/posts/1`);
  const userInfo = await promiseGet(`${url}/users/${userId}`);
  console.log(userInfo);
})();

프로미스의 정적 메서드

Promise.resolve / Promise.reject

: 이미 존재하는 값을 래핑하여 프로미스를 생성하기 위해 사용

Promise.all

: 여러 개의 비동기 처리를 모두 병렬 처리할 때 사용

  • 프로미스를 요소로 갖는 배열 등의 이터러블을 인수로 전달
  • 전달받은 모든 프로미스가 모두 fulfilled 상태가 되면 종료모든 처리 결과를 배열에 저장해 새로운 프로미스 반환
    • 첫 번째 프로미스가 가장 나중에 fulfilled 상태가 되어도 첫번째 프로미스가 resolve한 처리 결과부터 차례대로 배열에 저장해 그 배열을 resolve하는 새로운 프로미스를 반환 ⇒ 처리순서 보장
    • 인수로 전달받은 배열의 프로미스가 하나라도 rejected 상태 가 되면나머지 프로미스가 fulfilled 상태가 되는 것을 기다리지 않고 즉시 종료
  • 가장 늦게 fulfilled 상태가 되는 프로미스의 처리 시간보다 조금 더 길다
    const promise1 = () => new Promise(resolve => setTimeout(() => resolve(1), 3000));
    const promise2 = () => new Promise(resolve => setTimeout(() => resolve(2), 2000));
    const promise3 = () => new Promise(resolve => setTimeout(() => resolve(3), 1000));
    
// 세개의 비동기를 병렬로 처리
Promise.all([promise1(), promise2(), promise3()])
   .then(result => {
   console.log(result); // [1, 2, 3] => 약 3초보다 조금 더 소요
    })
   .catch(err => {
      console.error(err);
    })

Promise.race

: Promise.all 메서드와 동일하게 프로미스를 요소로 갖는 배열 등의 이터러블을 인수로 전달

  • 모든 프로미스가 fulfilled상태가 되는 것을 기다리는 것이 아니라 가장 먼저 fulfilled상태가 된 프로미스의 처리 결과를 resolve하는 새로운 프로미스를 반환
  • 전달된 프로미스가 하나라도 rejected 상태가 되면 에러를 reject하는 새로운 프로미스를 즉시 반환
    const promise1 = () => new Promise(resolve => setTimeout(() => resolve(1), 3000));
    const promise2 = () => new Promise(resolve => setTimeout(() => resolve(2), 2000));
    const promise3 = () => new Promise(resolve => setTimeout(() => resolve(3), 1000));
    
    Promise.race([promise1(), promise2(), promise3()])
    .then(result => {
    	console.log(result); // 3
    })
    .catch(err => {
    	console.error(err);
    })

Promise.allSettled

: 전달받은 프로미스가 모두 settled 상태가 되면 처리 결과를 배열로 반환

  • 상태와 상관없이 인수로 전달받은 모든 프로미스들의 처리 결과가 모두 담겨 있음

마이크로태스크 큐

 setTimeout(()=> console.log(1), 0);
    
  Promise.resolve()
   	.then(() => console.log(2))
    .then(() => console.log(3));
  • 프로미스는 후속처리 메서드도 비동기로 동작하므로 1→2→3 같아 보이지만 2→3→1 순으로 출력
    이유:
    마이크로태스크 큐는 태스크큐보다 우선순위가 높음!!!
    • 마이크로태스크 큐 - 후속처리 메서드 콜백함수 일시 저장
    • 테스크 큐 - 비동기 함수의 콜백함수나 이벤트 핸들러 일시 저장

따라서 이벤트 루프는 콜스택이 비면 마이크로태스크 큐 우선 실행 후 마이크로테스크 큐가 비면 태스크 큐에서 대기하고 있는 함수 실행

0개의 댓글