[JavaScript] 프라미스

Hyemin_12·2022년 12월 12일
0

JavaScript

목록 보기
6/6
post-thumbnail

프라미스

프라미스

프라미스란?

  • 제작 코드 : 시간이 걸리는 비동기 작업을 진행하는 코드
  • 소비 코드 : 제작 코드의 결과를 기다렸다가 이를 소비하는 코드
  • 프라미스 : 제작 코드와 소비 코드를 연결해주는 자바스크립트 객체

프라미스 객체 생성

let promise = new Promise(function(resolve, reject) {
	// ...
});
  • Primise에 전달되는 함수를 executor(실행자, 실행 함수)라고 함
  • executor의 인수인 resolve, reject는 자바스크립트에서 제공하는 콜백 함수
  • executor 함수 내에서 결과를 얻으면 성공, 실패에 따라 인수로 넘겨준 콜백 중 하나를 반드시 호출

resolve, reject

  • resolve(value) : 일이 성공적으로 끝났을 때 => 결괏값을 전달하며 호출
  • reject(error) : 일시 실패했거나 에러 발생 시 => 에러 객체를 전달하며 호출
  • executor 내부에서 한 번 호출되어 처리가 끝나면 더 이상 변하지 X
  • resolve, reject에는 인수를 하나만 전달 가능(전달하지 않아도 O)
  • Promiise를 호출하면 반환되는 프라미스 객체는 접근 불가능한 내부 숨김 속성을 가짐
    - state => 초기 상태 : pending, resolve 호출 시 : fulfilled, reject 호출 시 : rejected
    • result => 초기 상태 : undefined, resolve 호출 시 : value, reject 호출 시 : erorr

executor 함수 내부 작업 성공 시

  • resolve 반환
  • 작업이 성공적으로 처리됐을 때의 프라미스를 fulfilled promise라 함

excutor 함수 내부 작업 실패 시

  • reject 반환
  • 보통 reject의 인수로는 Error 객체나 Error를 상속받은 객체 전달(타입 제한은 X)

resolve든, reject든 일단 작업이 마무리 된 상태를 settled promise라고 함
작업이 진행중인 프라미스를 pending promise라고 함

then, catch, finally

  • 프라미스 객체는 executor 함수를 통해 처리된 결과 또는 에러를 받을 소비 함수를 이어주는 역할을 함
  • 결과나 에러를 소비하기 위해 프라미스에서 제공하는 메서드 : then, catch, finally

then

  • 첫 번째 인수는 프라미스가 이행됐을 때(resolved) 실행되는 소비 함수
  • 두 번째 인수는 프라미스가 거부됐을 때(rejected) 실행되는 소비 함수

resolve 함수 실행

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

reject 함수 실행

let promise = new Promise(function(resolve, reject) {
setTimeout(() => reject(new Error("에러 발생!")), 1000);
});
// reject 함수는 then의 두 번째 함수를 실행
promise.then(
result => alert(result), // 실행되지 않음
error => alert(error) // 1초 후 "Error: 에러 발생!"를 출력
);
  • 작업이 성공적으로 처리됐을 때만 코드를 실행하고 싶으면 첫 번째 함수만 전달해도 상관 X

catch

  • 에러가 발생한 경우만 다루고 싶을 시 then의 첫 번째 인수에 null을 주고 두 번째 인수로 reject 함수를 전달하거나 catch 메서드 사용
  • catch 메서드와 then(null, f)는 완전히 같음(catch가 더 간결함)
let promise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("에러 발생!")), 1000);
});
// catch(f)는 promise.then(null, f)과 동일하게 작동함
promise.catch(alert); // 1초 뒤 "Error: 에러 발생!" 출력

finally

  • 프라미스가 처리되면 결과에 상관없이 finally 메서드로 전달한 함수 무조건 실행
  • 로딩 화면을 멈추는 경우처럼 결과와 상관없이 실행할 마무리 작업이 필요할 때 사용
  • 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에서 에러 객체를 다룰 수 있음
  • 프라미스가 pending 상태이면 then, catch, finally 또한 처리 되기 전까지 대기
  • 프라미스가 이미 처리된 상태면 즉각 실행

프라미스 체이닝

  • 순차적으로 처리해야 하는 비동기 작업이 여러 개 있는 상황에 적용하는 방법
new Promise(function(resolve, reject) {
// (*)
setTimeout(() => resolve(1), 1000);
}).then(function(result) {
// resolve 호출의 결과로 전달된 값(1)이 result로 넘어옴 (**)
alert(result); // 1
// 여기서 2가 반환되지만, 내부적으로 반환값을 resolve 함수로 전달하는 Promise 객체를 생성하여 반환하므로, 결과적으로 연쇄적인 then 메서드 호출이 가능함
return result * 2;
// 위의 코드는 내부적으로는 아래와 같은 작업을 실행하는 코드로 바뀌어서 실행됨을 유의!
// return new Promise(resolve => resolve(result * 2));
}).then(function(result) {
// (***)
alert(result); // 2
return result * 2;
}).then(function(result) {
alert(result); // 4
return result * 2;
});
  • 프라미스 체이닝이 가능한 이유 : then의 호출 결과로 프라미스 객체를 반환하기 때문
    - 반환되는 프라미스 객체의 then 메서드를 연속해서 호출 가능
  • 동일한 프라미스 객체에 여러 번 then 메서드를 호출하면 체이닝이 일어나지 X
  • then 내부에서 새로운 프라미스 객체를 생성하여 반환 가능

fetch 함수

  • 네트워크 요청(대표적인 비동기 작업)을 보내는 함수
  • 요청 결과로 프라미스 객체를 반환

에러 핸들링

  • 프라미스가 거부되면 제일 가까운 catch로 넘어감

암시적 try .. catch

  • executor 함수와 프라미스 주위에는 보이지 않는 try .. catch 존재
  • 에외 발생 시 암시적 try .. catch에서 예외를 잡고 reject를 호출한 것과 같은 처리를 함
new Promise((resolve, reject) => {
// 예외 발생 (이 경우 reject 함수를 호출하며 해당 예외 객체를 전달한 것과 똑같은 결과
가 발생함)
throw new Error("에러 발생!");
// 다음과 같이 바깥에 보이지 않는 try - catch 블록이 존재한다고 생각하기
/*
try {
// ...
} catch(e) {
reject(new Error("에러 발생!"));
}
*/
})
.catch(alert); // Error: 에러 발생!
  • 모든 종류의 에러가 암시적 try .. catch에서 처리됨

다시 던지기

  • 체인 마지막의 catch 메서드는 다른 언어의 catch 블록과 유사
  • then 핸들러를 사용하다 마지막에 catch 한 번만 쓰면 then에서 발생한 모든 에러를 해당 catch에서 처리함
  • 처리할 수 없는 에러로 판단 시 throw 문으로 다시 에러 던지기 가능
profile
개발 블로그🌱

0개의 댓글

관련 채용 정보