Promise

gyu0714·2022년 10월 31일
3

javascript

목록 보기
1/3

Promise?

제작코드 (원격에서 스크립트를 불러우는 것 같은 시간이 걸리는일)와 소비코드 (제작코드의 결과를 기다렸다가 이를 소비한다. 이때 소비 주체(함수)는 여럿이 될 수 있다.)를 연결해주는 특별한 자바스크립트 객체이다.

let promise = new Promise(function(resolve, reject) {
  // executor (제작 코드, '가수')
});

new Promise에 전달되는 함수는 executor(실행함수)라고 부른다. executor는 new Promise가 만들어질 때 자동으로 실행되는데, 결과를 최종적으로 만들어내는 제작 코드를 포함한다.
executor의 인수 resolve와 reject는 자바스크립트에서 자체 제공하는 콜백이다. 개발자는 resolve와 reject를 신경 쓰지 않고 executor 안 코드만 작성 하면 된다.

  • resolve(value) - 일이 성공적으로 끝난 경우 그 결과를 나타내는 value와 함께 호출
  • reject(error) - 에러 발생 시 에러 객체를 나타내는 error와 함께 호출

요약하면 executor는 자동으로 실행되는데 여기서 원하는 일이 처리된다. 처리가 끝나면 executor는 처리 성공 여부에따라 resolve난 reject를 호출한다.
한편, new Promise 생성자가 반환하는 promise 객체는 다음과 같은 내부 프로퍼티를 갖는다.

  • state - 처음엔 pending (보류)상태에서 resolve 가 호출되면 fulfilled, reject 가 호출되면 rejected로 변한다.
  • result - 처음엔 undefined이었다 resolve(value) 가 호출되면 value로 reject(error) 가 호출되면 error로 변한다.
    따라서, executor는 아래 그림과 같이 promise의 상태를 둘 중 하나로 변화 시킨다.

💡💡Promise는 성공 또는 실패만 한다.
executor는 resolve나 reject 중 하나를 반드시 호출해야 한다. 이때 변경된 상태는 더 이상 변하지 않는다. 처리가 끝난 Promise에 resolve나 reject를 호출하면 무신된다.

💡💡Error 객체와 함께 거부하기
무언가 잘못된 경우, executor는 reject를 호출해야 한다. 이때 인수는 resolve와 마찬가지로 어떤 타입도 가능하지만 Error 객체 또는 Error를 상속받은 객체를 사용할 것을 추천한다.

💡💡resolve. reject 함수 즉시 호출하기
executor는 대개 무언가를 비동기적으로 수행하고, 약간의 시간이 지난 후에 resolve, reject를 호출하는데, 꼭 이렇게 할 필요는 없다. 아래와 같이 resolve나 reject를 즉시 호출 할 수 있다.

let promise = new Promise(function(resolve, reject) {
	resolve(123);
});

어떤 일을 시작했는데 알고 보니 일이 이미 끝나 저장까지 되어있는 경우, 이렇게 resolve나 reject를 즉시 호출하는 방식을 사용 할 수 있다.
이렇게 하면 Promise는 즉시 이행 상태가 된다.

💡💡 state와 result는 내부에 있다.
Promise 객체의 state, result 프로퍼티는 내부 프로퍼티이므로 개발자가 직접 접근할 수 없다.
.then/.catch/.finally 메서드를 사용하면 접근이 가능하다.

소비자: then, catch, finally

Promise 객체는 executor와 결과나 에러를 받을 소비함수를 이어주는 역할을 한다. 소비함후는 .then, .catch, .finally 메서드를 사용해 등록 된다.

then

.then은 Promise에서 가장 중요하고 기본이 되는 메소드이다.

promise.then(
	(result) => { // 결과를 다룬다. }
    (error) => { // 에러를 다룬다. }
)'

.then의 첫 번째 인수는 프라미스가 이행되었을 때 실행되는 함수이고, 여기서 실행 결과를 받는다.
.then의 두 번째 인수는 프라미스가 거부되었을 때 실행되는 함수이고, 여기서 에러를 받는다.

let promise = new Promise(function(resolve, reject) {
	setTimeout(() => resolve("완료!"), 1000);
});
    
// resolve 함수는 .then의 첫 번째 함수를 실행한다.
pomise.then(
	result => alert(result), // 1초후 완료!
    error => alert(error) // 실행되지 않음
)

catch

에러가 발생한 경우만 다루고 싶다면 .then(null, errorHandlingFunction) 같이 null을 첫 번째 인수로 전달하면 된다. .catch(errorHandlingFunction)를 써도 되는데, .catch는 .then에 전달하는 것과 동일하게 작동한다.

let promise = new Promise((resolve, reject) => {
	setTimeout(() => reject(new Error("error")), 1000);
});

// .catch(f)는 promise.then(null, f)과 동일하게 작동.
promise.catch(alert); // 1초 뒤 "error" 출력

finally

try {...} catch{...} 에 finally 절이 있는 것처럼, 프로미스에도 finally가 있다.
프로미스가 처리되면(이행 or 거부) f 가 항상 실행된다는 점에서 .finally(f) 호출은 .then(f, f) 과 유사하다. 쓸모가 없어진 로딩 인디케이터(loading indicator)를 멈추는 경우같이, 결과가 어떻든 마무리가 필요하면 finally가 유용하다.

new Promise((resolve, reject) => {
	// 시간이 걸리는 어떤일을 수행하고, 그 후 resolve, reject를 호출
})
	.finally(() => loading indicator 중지)
    .then(result => result와 error보여줌 => error 보여줌)

그런데 finally는 .then(f, f) 과 완전히 같진 않다.
1. finally 핸들러엔 인수가 없다. finally에선 프로미스가 이행되었는지, 거부되었는지 알 수 없다. finally에선 절차를 마무리하는 '보편적'동작을 수행하기 때문에 성공, 실패 여부를 몰라도 된다.
2. finally 핸들러는 자동으로 다음 핸들러에 결과와 에러를 전달한다.

new Promise((resolve, reject) => {
  setTimeout(() => resolve("결과"), 2000)
})
  .finally(() => alert("프라미스가 준비되었습니다."))
  .then(result => alert(result)); // <-- .then에서 result를 다룰 수 있음
new Promise((resolve, reject) => {
  throw new Error("에러 발생!");
})
  .finally(() => alert("프라미스가 준비되었습니다."))
  .catch(err => alert(err)); // <-- .catch에서 에러 객체를 다룰 수 있음

finally는 프로미스 결과를 처리하기 위해 만들어 진 게 아니다. 프로미스 결과는 finally를 통과해서 전달되고, 이런 특징은 자주 사용된다.
3. .finally(f)는 함수 f를 중복해서 쓸 필요가 없기 때문에 .then(f, f)보다 문법 측면에서 더 편리하다.

정리

💡💡 Promise
프로미스를 이용하면 흐름이 자연스럽다.
loadScript(script)로 스크립트를 읽고, 결과에 따라 그 다음(.then)에 무엇을 할지에 대한 코드를 작성하면 된다.
프로미스에 원하는 만큼 .then을 호출 할 수 있다.
.then을 호출하는 것은 새로운 함수를 리스트에 추가하는 것과 같다.

💡💡 Call back
loadScript(script, callback)를 호출할 때, 함께 호출할 callback 함수가 준비되어 있어야 한다.
loadScript를 호출하기 이전에 호출 결과로 무엇을 할지 미리 알고 있어야 한다.
💡 콜백은 하나만 가능하다.

profile
gyu0714

0개의 댓글