본문 바로가기
내마음대로만들어보자/React 프로젝트 만들기 이해

[React] Promise

by 소농민! 2021. 11. 22.
728x90

1. Promise 란?

자바스크립트는 싱글 쓰레드로 동작하는 언어이다.(싱글 쓰레드는 순차적으로 실행된다고 보면된다.)

자바스크립트는 하나의 요청이 완료될때까지 기다리지 않고 동시에 다른 작업을 실행하는 비동기 호출로 한번에 여러 요청을 처리할 수 있다.

 

자바스크립트가 실행될 때 다음과 같은 요소들이 실행을 도와준다.

- Call Stack : 자바스크립트에서 수행해야 될 함수들을 순차적으로 스택에 담아 처리

- Web API : 웹 브라우저에서 제공하는 API로 Ajax , Timeout 등의 비동기 작업을 실행

- Task Queue : Callback Queue 라고도 하며, Web API에서 넘겨받은 콜백함수를 저장

- Event Loop : Call Stack 이 비어있다면 Task Queue의 작업을 Call Stack으로 옮김

 

* 자바스크립트 동작원리 

setTimeout(() => console.log('async chanyeong'));
console.log('hello chanyeong');

// hello chanyeong
// async chanyeong

 

1) setTimeout 함수가 실행되며 Call Stack에 setTimeout 함수가 추가된다.

2) setTimeout 함수는 자바스크립트 엔진에서 처리하지않고 Web API가 처리한다. 

     setTimeout 함수는 Web API의 Timeout작업을 요청한 시간이 지나면 Task Queue로 인자로 받은 callback함수를 전달한다.

3) 두번째 라인에 작성한 console.log가 Call Stack에 추가된다. 그리고 Call Stack 에 추가된 console.log 가 실행되어 

     콘솔에 hello chanyeong 이라는 문자열이 출력된다.

4)  이때, Event Loop는 Call Stack이 비어있는지 확인을 한다.

5) 비어있다면 Task Queue에 있던 callback 함수를 Call Stack으로 옮겨 함수를 실행한다. 

     추가로 async chanyeong이 콘솔에 출력된다.

 

 

2. 콜백이란?

콜백은 자바스크립트가 비동기 처리를 하기 위한 패턴 중 하나

콜백 패턴을 사용하다보면 일명 콜백 헬로 불리는 엄청난 중첩 문제가 생기기 쉽다.

 

function async1('a', function (err, async2){

     if(err){

         errHandler(err);

     }else{ ...

          async2('b', function (err, async3){

              ... })

         {

              ...

         }

      }

});

 

* 발생 원인

 비동기 처리 시에는 실행 완료를 기다리지 않고 바로 다음 작업을 실행하는데 이말은 순서대로 코드를 쭉 적는다고 우리가 원하는 순서대로 작업이 이루어지지 않는다는 뜻이다.

비동기 처리 함수 내에서 처리결과를 반환하는 걸로는 원하는 동작을 하지 않으니 콜백함수를 사용해 원하는 동작을 하게 하려고 콜백 함수를 쓰는데 콜백 함수 내에서 또 다른 비동기 작업이 필요한 경우 위와같은 중첩이 발생할 수 있다.

 

3. Promise 란?

비동기 연산이 종료된 이후 결과를 알기 위해 사용하는 객체

프라미스를 사용하면 비동기 메서드를 마치 동기 메서드처럼 값을 반환 할 수 있다.

콜백 헬 때문에 ES6에서 도입한 또다른 비동기 처리 패턴이다.(비동기 처리 시점을 좀더 명확하게 할 수 있다.)

 

* 프라미스 생성

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

     if(...) {

        ...

        resolve("succes");

      }else{

         reject("fail");

      }

   });

- 프라미스 객체를 만든다(new 키워드)

- 비동기 작업을 수행할 콜백 함수를 인자로 전달받아서 사용한다.

   인자로는 (resolve, reject) => {} 이런 excutor 실행자(혹은 실행 함수)를 받는다.

- 이 실행자는 비동기 작업이 끝나면 바로 두가지 콜백 중 하나를 실행한다.

- resolve : 작업이 성공한 경우 호출할 콜백

- reject : 작업이 실패할 경우 호출할 콜백 

 

* 프라미스의 상태값

- pending : 비동기 처리 수행 전(resolve, reject가 아직 호출되지 않음)

- fulfilled : 수행 성공(resolve가 호출된 상태)

- rejected : 수행 실패(reject가 호출된 상태)

- settled : 성공 or 실패(resolve 나 reject가 호출된 상태)

 

* 프라미스 후속 처리 메서드

- 프라미스로 구현된 비동기 함수는 프라미스 객체를 반환

- 프라미스로 구현된 비동기 함수를 호출하는 측에서는 이 프라미스 객체의 후속 처리 메서드를 통해 비동기 처리 결과를 받아서 처리한다.

- .then(성공시, 실패시)

   then의 첫 인자는 성공 시 실행, 두번째 인자는 실패 시 실행된다.(첫번째 인자만 넘겨도 된다.)

 

// 프라미스를 하나 만든다.
let promise = new Promise((resolve, reject) => {
	setTimeout(() => resolve("완료!"), 1000);
});

// resolve
promise.then(result => {
	console.log(result); // 완료! 가 콘솔에 찍힌다. 
}, error => {
	console.log(error); // 실행되지 않는다.
});
// 프라미스를 하나 만든다.
let promise = new Promise((resolve, reject) => {
	setTimeout(() => reject(new Error("오류!")), 1000);
});

// reject
promise.then(result => {
	console.log(result); // 실행되지 않는다
}, error => {
	console.log(error); // Error: 오류!가 실행된다. 
});

- .catch 실패 시

// 프라미스를 하나 만든다. 
let promise = new Promise((resolve, reject) => {
	setTimeout(() => reject(new Error("오류!"), 1000);
});

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

 

* promise chaining (프라미스 체이닝)

프라미스는 후속 처리 메서드를 체이닝 해서 여러 개의 프라미스를 연결할 수 있다.(이걸로 콜백 헬을 해결 할 수 있다.)

후속 처리 메서드 (then)을 쭉 이어 준다.

new Promise((resolve, reject) => {
	setTimeout(() => resolve("promise 1"), 1000);
}).then((result) => { // 후속 처리 메서드 하나를 쓰고,
	console.log(result); // promise 1
	return "promise 2";
}).then((result) => { // 이렇게 연달아 then을 써준다. 
	console.log(result);
	return "promise 3";
}).then(...);

※ 프라미스 사용에 대한 Tip

1) async

- 함수 앞에 async를 붙여서 사용한다.

- 항상 프라미스를 반환한다. (프라미스 아닌 값이라도 프라미스로 감싸서 반환해준다.)

// async는 function 앞에 써준다.
async function myFunc() {
	return "프라미스를 반환!"; 프라미스가 아닌걸로 반환해보자.
}

myFunc().then(result => {console.log(result)});

2) await

- async의 짝꿍( async 없이 사용못한다)

- async 함수 안에서만 작동한다

- await는 프라미스가 처리될 때 까지 기다렸다가 그 이후에 결과를 반환한다.

async function myFunc(){
	let promise = new Promise((resolve, reject) => {
		setTimeout(() => resolve("완료!"), 1000);
	});

    console.log(promise);

	let result = await promise; // 여기서 기다리자!하고 신호를 주면 

    console.log(promise);

	console.log(result); // then(후처리 함수)를 쓰지 않았는데도, 1초 후에 완료!가 콘솔에 찍힌다. 
}

await를 만나면 실행이 잠시 중단되었다가 프라미스 처리 후에 실행을 재개한다.