[JavaScript] Asynchronous - Promise

Steve·2021년 5월 20일
0

웹개발 코스

목록 보기
26/59

Promise 객체

  • Promise 객체 생성
  • Promise 의 State, Result

Consumers

  • then
  • catch
  • finally

Promise 객체

Promise 는 "약속하다" 라는 뜻이다. 약속하다라는 이름이 붙은 이유는 값을 보장하기 때문이다. 어떤 작업이 실패하든 성공하든, Promise 객체는 성공하면 값을 가지고, 실패하면 에러 값을 가진다. 결국 성공하든 실패하던 무조건 결과를 가지게 된다.

비동기 작업의 코드는 두 가지로 나뉜다.
Producing code 는 시간이 걸리는 코드이다. 예를 들면 서버에서 데이터를 불러오는 코드다.
Consuming code 는 producing code 의 결과를 원하는 코드이다. 예를 들면 서버에서 불러온 닉네임을 주황색으로 만들고자 하는 코드이다.

Promise 는 producing code 와 consuming code 를 연결해주는 자바스크립트 객체이다. 비유를 하자면 Promise 는 "구독자 리스트"이다. producing code 가 시간을 들여 결과를 내면, Promise 는 그 결과를 원하는 consuming code 들 (구독자들) 이 결과를 사용할 수 있도록 제공한다.

Promise 객체 생성하기

let promise1 = new Promise(()=> (resolve, reject)) {
  // executor (producing code)
  resolve(result); // 성공 시
  reject(error); // 실패 시
}

Promise 객체는 new 키워드를 이용하여 생성할 수 있다. 생성자(constructor)의 인자로 annonymous function 이 들어가는데, 이 함수를 executor 라고 한다. Promise 객체가 생성되는 순간 executor 는 자동으로 실행된다. Executor 는 producing code 를 가지며, 어떠한 결과를 내놓을 것이다.

Executor 는 반드시 두 개의 callback 함수를 인자로 가져야 한다.
resolve(value) - 작업이 성공했을 시 호출될 함수. 인자 : 결과값.
reject(error) - 작업이 실패했을 시 호출될 함수. 인자 : 에러 객체.

Promise 객체의 모양은 전에 callback 으로 error handling 을 만들었던 것과 비슷하다. 즉 Promise 는 error handling 과 chaining 을 JavaScript 자체에서 지원하는 것이라고 볼 수 있다.

Promise 의 state 와 result

Promise 객체는 state(상태)와 result(결과) 라는 두 속성을 가진다.
3가지의 상태가 있고, 상태에 따라 결과값이 다르다.
(Promise 객체를 console.log() 출력하면 두 속성의 상태를 볼 수 있다.)

stateresult
초기값 : "pending"undefined
resolve 호출 시 : "fulfilled"value
reject 호출 시 : "rejected"error

(Promise 의 속성에 직접 접근할 수는 없다.)

Executor 가 resolvereject 둘 중 하나를 호출하기 전까지 Promise 객체의 상태는 pending 이다.

// State: <Pending>
let promise1 = new Promise((resolve, reject)=> {});
console.log(promise1);
// Promise {State: "pending", Result: undefined}

// State: <fullfilled>
let promise2 = new Promise((resolve, reject)=> { 
  resolve("Hello"); 
});
console.log(promise2);
// Promise {State: "fullfilled", Result: "Hello"}

// State: <rejected>
let promise3 = new Promise((resolve, reject)=> { 
  reject(new Error("Whoops!"); 
});
console.log(promise3);
// Uncaught (in promise) Error: Whoops!
// reject()의 인자로 아무 값이나 넣을 수 있지만 Error 객체를 사용하는 것이 권장된다.
// Error 객체는 나중에 알아보자.

resolved 되거나 rejected 된 Promise 는 "setteled" 되었다고 부른다.

Consumers: then, catch, finally

앞서 말했듯 Promise 객체는 executor (producing code) 와 executor 가 내놓은 결과를 원하는 consuming code 사이를 연결해주는 역할을 한다. Consuming code 는 .then(), .catch(), .fianlly() 로 등록을 할 수 있다. 이 세 함수는 Promise 객체의 메서드이다. 즉 Promise 인스턴스로만 호출할 수 있다.

then

가장 핵심적인 함수이다. then() 에는 두개의 인자가 들어간다.
첫번째 인자는 Promise 가 resolved 되었을 때 실행될 함수이다. 이 함수는 결과값을 인자로 받아 필요한 작업을 수행한다.
두번째 인자는 Promise 가 rejected 되었을 때 실행될 함수이다. 이 함수는 error 를 인자로 받는다.

promise1.then(
  (result)=> { /* result 를 가지고 필요한 작업을 수행 */  },
  (error)=> { /* error 를 처리 */ }
)

.then 을 사용한 예제는 다음과 같다.

// Promise 가 resolve 되었을 때
let promise1 = new Promise((resolve, reject)=> {
  setTimeout(()=> { resolve("done!"), 1000 });
});

// Promise 가 reject 되었을 때
let promise1 = new Promise((resolve, reject)=> {
  setTimeout(()=> { reject(new Error("Whoops!")), 1000 });
});

promise1.then(
  (result)=> { console.log(result) },
  (error)=> { console.log(error) }
)

// 혹은 resolved 되었을 때만 관심이 있다면 then 에 함수를 하나만 제공할 수도 있다.
promise.then(console.log); 
// console.log 가 인자 없이도 작동이 가능하다는건 처음 알았다. alert 도 마찬가지였다.

catch

// 에러가 나는지만 체크하고 싶다면, then 의 첫 번째 인자로 null 을 넣어도 된다.
let promise1 = new Promise((resolve, reject) => {
  setTimeout(() => reject(new Error("에러났어요.")), 1000);
});

promise1.then(null, console.log);
promise1.catch(console.log);

// 위 두 코드는 같은 동작을 수행한다.
// catch 는 내부적으로 then(null, f) 로 설계되어 있다. 
// catch is an "analog" of then(null, f)

만약 catch 가 chain 의 중간에 있다면, catch 전에 에러가 날 경우 catch 가 실행되고, catch 이후에 then 들이 호출된다. (추가적인 catch 가 없다면 에러를 잡아낼 수 없다.)

만약 catch 가 chain 의 에 있다면, 중간에 에러가 날 경우 catch 가 실행된 후 에러 이후의 코드들은 작동하지 않는다.

Promise 에서 에러 핸들링은 try...catch 와 비슷하게 작동한다.

Error handling | JavaScript.info - try catch 에 관해
Error handling with promises | JavaScript.info - Promise 와 try catch 와의 관계

finally

finally 는 Promise 의 결과가 resolve 든 reject 든 무조건 수행해야 할 작업이 있을 때 사용한다. 예를 들어 성공하든 실패하든 로딩화면을 끝내야 할 때, finally() 안에 로딩창을 없애는 코드를 넣으면 된다. 어쨌든 finally 도 then, catch 와 마찬가지로 Promise 가 setteled 된 후에 실행된다.

promise1.finally(()=> { 로딩화면 종료하기 })
        .then((result)=> { result 보여주기 },
              (error)=> { error 보여주기 })

finally 의 annoymous function 은...

  1. 인자가 없다. 즉 Promise 로부터 아무런 값도 전달받지 않는다.
  2. Promise 의 결과를 다음 핸들러로 넘긴다.

Promise chaining
https://javascript.info/promise-chaining

profile
게임과 프론트엔드에 관심이 많습니다.

0개의 댓글