개념정리 - 비동기 Promise

Seungmin Shin·2021년 5월 20일
1

코딩 개념정리

목록 보기
8/33

Promise

- Promise?

사람과 사람간의 '약속' 이란 서로 앞으로의 일을 어떻게 할 것인가를 미리 정하는것이다.

"내일 2시에 OOO에서 만나."

"오늘은 일찍 퇴근하기로 약속해."

우리가 일상에서도 많이사용하는 이 약속이 과연 자바스크립트 안에서는 쓰이는 것일까?

Promise는 비동기를 간편하게 처리할 수 있도록 도와주는 자바스크립트에서 제공하는 Object이다.

Promise는 정해진 장시간의 작업을 수행하고 나서

정상적으로 기능이 수행되어졌다면 성공의 메세지와 결과값을 리턴하고

예상치못한 오류가 발생했다면 에러를 전달해준다.

예를들어 학원수강신청을 하려고 하는데, 아직 개강을 안한 상태라서 당장은 신청할 수 없는 상태일때,

몇몇의 학원에서는 이메일 등을 통해서 미리 등록을 할 수 있게 하는 시스템을 가지고 있다.

한 수강생이 아직 개강전의 수업에 대한 수강신청을 하려고 이메일을 등록해 놓았다,

이 학생에게는 당장은 결과를 담은 메일을 회신하지 않는다. 아직 개강을 하지 않았기 때문이다.

그리고 몇일 후 수업이 개강을 하면, 그 즉시 사전등록을 한 수강생에게 순차적으로 메일이 발송된다.

그리고 수업이 이미 오픈된 이후에 수강생이 수강신청을 하려고하면 이 수강생은

기다릴 필요 없이 메일이 바로 전송될 것이고 바로 수업에 들어갈 수 있게 된다.

이것이 Promise의 기본 개념이다, 자바스크립트 안에서 약속이 일어나는것이다.

'지금 이메일주소 하나를 전송해줄테니, 지금 말고 나중에 개강을 하면 이 메일로 공지메일을 전송해.'

라는 약속이 일어나고, 그 약속을 수행할 수 있게 해주는것이 바로 Promise Object이다.

- Promise 이해 포인트

Promise를 사용할땐, 두가지의 포인트에 초점을 둬야한다.

첫번째는 State, 상태이다.

프로세스가 현재 어떤상태인지를 아는것이 중요하다, 현재 어느정도의 명령을 수행중인지,

그리고 그 작업이 종료됬으면 성공적으로 종료됬는지 아니면 오류가 발생해서 실패했는지

이해하는것이 중요하고.

두번째는 Producer, Consumer 의 차이점을 아는것이다.

우리가 원하는 데이터를 제공하는 쪽과, 이 제공된 데이터를 쓰는 쪽의 차이를 잘 알면 된다.

// state: pending => fulfilled or rejected
// Producer vs Consumer

- Promise 생성

1. Producer

우리가 원하는 기능을 비동기적으로 수행하는 Promise를 만들어보자.

const promise = new Promise()

promise라는 변수안에 Promise를 만들어준다. Promise는 클래스기 때문에

new라는 키워드를 붙여 생성할 수 있다. (변수 이름 promise는 변경가능하다.)

이때, Promise 에 마우스를 올려보면

이런 설명이 뜬다. Promise 의 생성자로 엑스큐터(executor) 라는 콜백함수를 전달해줘야하는데,

이 콜백함수는 두가지 또다른 콜백함수를 받는다. resolvereject이다.

resolve는 기능을 정상적으로 수행해서 마지막에 최종데이터를 전달하는 함수이고

reject는 기능을 수행하다가 중간에 문제가 생기면 오류를 호출해주는 함수이다.

그럼 이제 new Promise() 에 resolve 와 reject가 들어가있는 executor 함수를 넣어보자.

const promise = new Promise((resolve,reject) => { 
 
})

이런식으로 작성할 수 있겠다. 이제 우리는 Promies를 생성할 수 있게 되었다.

-여담-
보통 Promise 함수 안에서는 heavy한 일들이 많이 일어난다고 한다. 왜냐하면,
이 함수는 보통 네트워크 작업에 사용되는데, 네트워크 안에서 데이터를 받아오거나
파일에서 큰 데이터를 받아오는것은 시간이 꽤 걸리는 작업일것이다.
만약 이런 일을 동기적으로 처리한다면 어떨까??
시간이 꽤 걸리는 저 작업이 종료될때까지 다른 코드들은 실행되지 않는다.
극단적인 예로 만약 어떤 데이터을 처리하는데 1시간이 걸린다고 하면
이 작업이 동기적일경우, 우리는 1시간동안 그 작업이 끝나기만을 기다려야 하는것이다.
이렇기 때문에 비동기적 프로그래밍인 Promise를 사용한다.

- Promise 동작

위에서 만들었던 new Promise 에 console.log를 추가해 어떻게 동작하는지 보자.

const promise = new Promise((resolve,reject) => { 
  console.log('Hello, world.')    // 문자열 'Hello, world.'를 출력해보자.
})

이렇게 console.log 에 문자열 'Hello, world.'를 넣고 콘솔창에서 돌려보면

이미지라 언제 뜨는지는 볼 수 없었지만, 엔터를 누르는 순간, 바로 Hello, world. 가 출력됐다.

자, 여기서 우리는 한가지 정보를 알 수 있다.

아, Promise는 기본적으로 결과값을 '바로' 반환해주는구나.

그렇다면, 이 정보로 인해 우리는 어떤 사실을 파악할 수 있을까?

만약 이 Promise 안에 네트워크 통신을하는 코드를 작성했다고 생각해보자.

그렇다면 Promise가 만들어지는 순간, 네트워크 통신이 발생해버린다.

그런데 그 네트워크 요청을 사용자가 요구했을때만 발생이 되야하는 경우라면?

즉, 사용자가 버튼을 눌렀을때 네트워크 요청이 들어와야하는 경우라면?

저런식의 코드는 사용자가 요구하지도 않았는데 불필요한 네트워크 통신이 일어날 수 있다.

그래서 우리는 이 점을 유의해서 학습해야 한다.

// when new Promise is created, the executor runs automatically.

const promise = new Promise((resolve,reject) => { 
  console.log('Hello, world.') 
})

- Promise, setTimeout

그렇다면 이제 네트워크 통신을 하는 것처럼 setTimeout을 이용해서 시간의 딜레이를 줘보자.

setTimeout 을 이용해서 우리가 원하는 콜백함수를 2초정도 딜레이를 시켜볼것이다.

const promise = new Promise((resolve,reject) => { 
  console.log('Hello, world.');
  setTimeout(() => {}, 2000);   // 밀리세컨드를 이용한다. (2000 -> 2초)
})

일단 이렇게 setTimeout 코드를 넣고, 안에 콜백함수를 넣게 되는데,

이 부분에서 resolve 와 reject 가 들어간다.

기능을 잘 수행해서 성공적으로 출력할때는 resolve 를 사용할것이고,

실패하여 오류를 출력해야 할 때는 reject 를 사용할것이다.

코드를 짜보자.

const promise = new Promise((resolve,reject) => { 
  console.log('Hello, world.');
  setTimeout(() => {
    resolve('kim');   // 데이터로 kim이라는 이름을 가져왔다고 가정한다.
  }, 2000);
})

자, 이 코드를 해석하자면,

어떤 일을 2초정도 무언가를 하다가 결국 일을 잘 마무리해서 resolve 안의
kim이라는 이름이 호출되게 한다.

라는 의미를 가지게 되었다. 생성자 Producer 를 만드는데 성공한것이다.

이제 이 생성자가 만들어놓은 Promise를 사용할 Consumer 를 만들어보자.

2. Consumers

Consumer 는 then 이나 catch 등으로 값을 받아올 수 있다.

promise.then(value => {
  console.log(value);
})
/*
promise라는 함수가 정상적으로 잘 수행이 되어서 값을 출력한다면,
.then -> 그러면
value -> 그 값을 가져올거야. 그래서 그걸로 내가 원하는 기능을 수행할꺼야.
*/

여기서 주어지는 value 라는 파라미터는, 위의 promise 함수에서

정상적으로 출력된 resolve 의 값이 들어가게 된다. (저기서는 kim)

한번 잘 나오는지 콘솔로 출력해보자.

직접 콘솔을 돌려보면 저 밑의 kim이 2초뒤에 출력되는걸 확인할 수 있을것이다.

그렇다면 reject 는 어떨까?

우리가 저 위의 작업이 어떤이유로 실패하게 되어 reject로 오류를 출력하는걸 해보자.

const promise = new Promise((resolve,reject) => { 
  setTimeout(() => {
    reject(new Error());   // Error()는 자바스크립트에서 제공하는 object중 하나이다.
  }, 2000);
})

이렇게 reject를 이용해서 new Error 를 만들어줬다.

err가 났을때, 어떤 err인지 설명해줘야한다. 설명도 한번 적어줘보겠다.

const promise = new Promise((resolve,reject) => { 
  setTimeout(() => {
    reject(new Error('no network'));  // network 가 없다는 오류 문구를 넣어본다.
  }, 2000);
})

자 이제 한번 돌려보자.

우리가 쓴 'no network' 라는 문구가 뜨며 에러가 발생하는걸 볼 수 있다.

오류코드를 잘 보면, Uncaught (잡히지 않은) 에러가 발생했다. 라고 적혀있다.

이때는 에러핸들링을 해봐야한다, 이때 사용되는 녀석이 catch 이다.

아까 작성했던 then 밑에 catch 를 작성해보자.

promise
  .then(value => {    // then을 이용해서 성공적인 케이스를 잘 출력했다면,
  console.log(value);
  })
  .catch(error => {   // catch를 이용해서, 오류 발생시 어떻게 처리할것인지를 만들면 된다.
  console.log(error);
  });

이렇게 코드를 작성하고 난 다음, 다시 콘솔을 돌려보면.

이렇게 에러가 뜨지 않고, 우리가 작성한 에러코드가 콘솔로 출력되는 걸 볼 수 있다.

에러가 났다고 해도, 에러가 난 상황을 사용자에게 보여줘야하기 때문에

우리가 평상시에 보던 빨간 에러가 나면 안되는것이다.

아, 그리고 혹시나

promise
  .then(value => { 
  console.log(value);
  })
  .catch(error => { 
  console.log(error);
  });

이 코드의 작성법에 대해 의문이 드는 사람들을 위해 간단하게 설명하자면,

promise 에 then을 호출하게 되면, 이 then 은 또 다시 promise 를 호출하게 된다.

그렇기 때문에, 그 밑에다가 바로 .catch 를 등록할 수 있는것이다.

한줄 요약

Promise object 를 만들때는 우리가 비동기적으로 작성하고 싶은 코드를
Producer로 작성했던 구역에 코드를 작성하고 나서
성공적으로 완료됬다면 resolve를, 실패했다면 reject를 전달하게 되는데
그 후에 then 과 catch 를 이용해서 성공한 값이나 실패한 에러를 받아와서
우리가 원하는 방식으로 처리하면 된다.

(출처: https://www.youtube.com/watch?v=JB_yU6Oe2eE) _드림코딩

profile
Frontend Developer

0개의 댓글