16. Promise

조뮁·2022년 8월 8일
0

JS중급

목록 보기
16/18
post-thumbnail

Promise

: 비동기 작업의 최종 완료 또는 실패를 나타내는 객체

  • promise = new Promise() 로 생성
const pr = new Promise((resolve, reject) => {
  // resolve : 성공한 경우 실행되는 함수
  // reject : 실패한 경우 실행되는 함수
});

참고 : resolve(), reject() = callback 함수

  • callback 함수 : 어떤 일이 완료된 이후 실행되는 함수

promise() 장점

https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Using_promises

  • 콜백은 자바스크립트 Event Loop가 현재 실행중인 콜 스택을 완료하기 이전에는 절대 호출되지 않습니다.
  • 비동기 작업이 성공하거나 실패한 뒤에 then() 을 이용하여 추가한 콜백의 경우에도 위와 같습니다.
  • then()을 여러번 사용하여 여러개의 콜백을 추가 할 수 있습니다. 그리고 각각의 콜백은 주어진 순서대로 하나 하나 실행되게 됩니다.

new Promise 생성자가 반환하는 promise 객체 : state, result를 프로퍼티로 가짐

(1) 초기

  • state : pending(대기)
  • result : undefined

(2) resolve(value) - 성공

  • state : fulfilled (이행됨)
  • result : value

(3) reject(error) - 실패

  • state : rejected(거부됨)
  • result : error
// (2) 성공을 가정한 코드
const pr = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Ok');  // resolve = 성공 시 실행할 함수
  }, 2000);
});

// (3) 실패를 가정한 코드
const pr2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(new Error('error...'))
  }, 2000)
});

(1) 초기

  • state : pending(대기)
  • result : undefined

(2) resolve(value) - 성공

  • state : fulfilled (이행됨)
  • result : 'OK'
    // 2초 후, state는 fulfilled로 바뀌고, result는 'OK'가 된다.

(3) reject(error) - 실패

  • state : rejected(거부됨)
  • result : error
    // 2초 후, state는 rejected로 바뀌고, result는 'error'가 된다.

여기까지가 판매자의 코드라고 볼 수 있다.

  • 판매자는 주문을 받으면 그 주문을 처리하고, 결과가 성공인지 실패인지 알려줌.

promise 결과 받기

.then

const pr = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('OK')
  }, 2000)
});

pr.then(
  // then을 이용하여 resolve와 reject를 처리할 수 있음
  function(result){
    console.log(result + '완료되었어!');
  },  
  // provise 함수가 resolve로 실행되었기 때문에, 두 번째 callback은 실행되지 않음.
  function(err){
    console.log(err + '실패.. 다시 주문해주세요');
  }
)

// OK완료되었어!

첫 번째 함수 function(result){}

  • promise가 이행되었을 때 실행되는 함수
  • result -> 'OK' 라는 값이 들어옴
const pr = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(new Error('err..'))
  }, 2000)
});

pr.then(
  // then을 이용하여 resolve와 reject를 처리할 수 있음
  function(result){
    console.log(result + '완료되었어!');
  },  
  function(err){
    console.log(err + '실패.. 다시 주문해주세요');
  }
)
// Error: err..실패.. 다시 주문해주세요

두 번째 함수 function(err){}

  • promise가 실패하였을 때 (거부되었을 때) 실행되는 함수
  • err : error 값이 들어옴

catch, finally

const pr = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(new Error('err..'))
  }, 2000)
});

pr.then(
  // then을 이용하여 resolve와 reject를 처리할 수 있음
  function(result){
    console.log(result + '완료되었어!');
  }
).catch(
  function(err){
    console.log(err + '실패.. 다시 주문해주세요');
  }
).finally(
  function(){
    console.log('--영업 끝났습니다--');
  }
)
// "Error: err..실패.. 다시 주문해주세요"
// "--영업 끝났습니다--"

catch : reject인 경우에만 실행됨

  • .then() 만 사용하는 것 보다, .catch()를 함께 쓰는것이 더욱 좋음
    • 코드를 명확하고 가독성이 좋게 사용할 수 있음
    • 첫 번째 함수 실행 시 발생할 수 있는 에러도 잡을 수 있음.

finally : 성공, 실패 여부와 관계없이 처리가 완료되면 항상 실행됨

  • ex) 로딩화면을 끝낼 때 사용할 수 있음

실습 1

const pr = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('OK');  // (1)
    // reject('err..')  // (2) 
  }, 1000);
});

console.log('시작');

pr.then((res) => {
  console.log(res);
}).catch((err) => {
  console.log(err);
}).finally(() => {
  setTimeout(() => {
    console.log('끝')
  }, 1000);
});

(1)
"시작"
"OK" // 1초 후
"끝" // 1초 후

(2)
"시작"
"err.." // 1초 후
"끝" // 1초 후


chaining

  • 순차적으로 각각의 작업이 이전 단계 비동기 작업이 성공하고 나서 그 결과값을 이용하여 다음 비동기 작업을 실행해야 하는 경우를 의미

실습 2 (여러 비동기 작업을 연속적으로 수행하는 방법)

  • callback hell
const f1 = function(callback){
  setTimeout(function(){
    console.log('1번 주문 완료');
    callback();
  }, 1000);
}

const f2 = (callback) => {
  setTimeout(function(){
    console.log('2번 주문 완료');
    callback();
  }, 3000);
};

const f3 = (callback) => {
  setTimeout(function(){
    console.log('3번 주문 완료');
    callback();
  }, 2000);
};

console.log('시작');
// 1번주문 후, 2번주문 후, 3번주문 할 것
// f1은 callback함수를 받기 때문에, f1 안에서 함수를 실행해줘야 한다. 
// 우리는 f1이 끝난 후 f2를 실행할 것이기 때문에, f1의 함수 내 f2를 실행해줘야 함. (f2 -> f3도 동일)
f1(function(){
  f2(function(){
    f3(function(){
      console.log('끝')
    });
  });
});

// "시작"
// (1초 후) "1번 주문 완료" 
// (3초 후) "2번 주문 완료"
// (2초 후) "3번 주문 완료"
// "끝"
  • callback hell을 promise로 변경
const f1 = () => {
  const apiResult = {
    score: 99,
    rating: 1,
  };
  return new Promise((res, rej) => {
    setTimeout(() => {
	// setTimeout()을 사용해 비동기 코드를 흉내낸 것.
	// 실제로는 여기서 XHR이나 HTML5 API를 사용할 것임
      res(apiResult);
      // api등 요청에 성공하면 받아와지는 결과값을 전달
    }, 1000);
  });
};

const f2 = (msg) => {
  console.log(msg);
  return new Promise((res, rej) => {
    setTimeout(() => {
      res('2번 요청 완료');
      // rej('xxx 2번 요청 실패 xxx');
    }, 3000);
  });
};

const f3 = (msg) => {
  console.log(msg);
  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(err => console.log(err))
  .finally(() => {
    console.log("--요청 끝--");
  })


// 시작
// (1초 후) {score: 99, rating: 1}
// (3초 후) 2번 요청 완료
// (2초 후) 3번 요청 완료
// "--요청 끝--"

// --- f2 가 실패한 경우 ---
// (1초 후) {score: 99, rating: 1}
// (3초 후) "xxx 2번 요청 실패 xxx"
// "--요청 끝--"
  • 실패한 프로미스가 있는 경우, 그 다음 프로미스는 시행되지 않음. (2번 주문이 실패한 경우, 3번 주문은 시도도 되지 않음.)

Promise.all([])

: 모든 주문을 동시에 실행하여, 모든 프로미스 값 or 거절된 프로미스 값 반환

  • 모든 프로미스 성공 시, 매개변수로 제공한 프로미스 각각의 이행 값을 모두 모아놓은 배열로 반환.
  • 실패 시, 매개변수의 프로미스 중 실패 사유를 그대로 반환
  1. 성공 시 배열로 결과값 반환
const f1 = () => {
  const apiResult = {
    score: 99,
    rating: 1,
  };
  return new Promise((res, rej) => {
    setTimeout(() => {
      res(apiResult);
    }, 1000);
  });
};

const f2 = (msg) => {
  console.log(msg);
  return new Promise((res, rej) => {
    setTimeout(() => {
      // res('2번 요청 완료');
      rej('xxx 2번 요청 실패 xxx');
    }, 3000);
  });
};

const f3 = (msg) => {
  console.log(msg);
  return new Promise((res, rej) => {
    setTimeout(() => {
      res('3번 요청 완료');
    }, 2000);
  });
};


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

// [{…}, '2번 요청 완료', '3번 요청 완료']
/*
0: {score: 99, rating: 1}
1: "2번 요청 완료"
2: "3번 요청 완료"
*/
  1. 프로미스 실패 시 reject 값 반환
const f2 = (msg) => {
  console.log(msg);
  return new Promise((res, rej) => {
    setTimeout(() => {
      // res('2번 요청 완료');
      rej('xxx 2번 요청 실패 xxx');
    }, 3000);
  });
};


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

// xxx 2번 요청 실패 xxx
  • 실패 시 배열값 반환 X , 실패한 error 값만 반환됨
  • 실패 시 .then() 안에 두 번째 함수 작성하고나 .catch() 사용해주지 않을 시 error 발생
    Uncaught (in promise) xxx 2번 요청 실패 xxx
  • 사용 예시 : 하나의 정보라도 누락될 시 전체 페이지를 보여주지 않아야 할 때 사용하면 좋다.

Promise.race([])

: 주어진 모든 프로미스 중 가장 먼저 완료되는 프로미스 값 반환

const f1 = () => {
  const apiResult = {
    score: 99,
    rating: 1,
  };
  return new Promise((res, rej) => {
    setTimeout(() => {
      res(apiResult);
    }, 1000);
  });
};

const f2 = (msg) => {
  console.log(msg);
  return new Promise((res, rej) => {
    setTimeout(() => {
      // res('2번 요청 완료');
      rej('xxx 2번 요청 실패 xxx');
    }, 500);
  });
};

const f3 = (msg) => {
  console.log(msg);
  return new Promise((res, rej) => {
    setTimeout(() => {
      res('3번 요청 완료');
    }, 2000);
  });
};


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

// {score: 99, rating: 1}
  • f2() 는 실패하는 프로미스이지만, race()에서 f1이 가장 빠르게 처리됐기 때문에, 그 이후 프로미스들은 살펴보지 않음.
  • 사용 예시 : 용량이 큰 여러 이미지들을 로딩할 시, 하나라도 먼저 완료된 이미지를 가져오는 경우 사용 가능

0개의 댓글