Promise

연꽃·2023년 1월 1일
1

Javascript

목록 보기
1/1

비동기 처리

기본적으로 작성한 코드는 위에서 아래로 내려가며 차례대로 실행된다. 그런데 코드 중 외부에 요청을 보내고 응답을 받는 코드나 처리하는 시간이 오래 걸리는 코드가 있다면 그 다음에 존재하는 코드는 시간이 오래걸리는 코드가 모두 완료된 뒤에 실행된다. 이것이 동기 처리이다.

반대로 비동기 처리는 시간이 오래 걸리는 코드가 완료되지 않더라도 다음 코드를 먼저 실행하는 방식을 말한다. 아래의 코드를 살펴보자.

console.log('start')
console.log('processing...')
console.log('end')

위 코드를 실행하면 다음과 같은 결과가 나온다.

start
processing...
end

이와 비교하기 위해 다음 코드를 살펴보자.

console.log('start')
setTimeout(()=> console.log('processing...'),1000)
console.log('end')

위 코드의 결과는 다음과 같다.

start
end
processing...

중간에 setTimeout(()=> console.log('processing...'),1000) 이 함수는 첫번째 인자로 실행하고자 하는 함수를 받는다. 두번째 인자로 ms단위의 시간을 받아, 두번째 인자만큼의 시간 후에 첫번째 인자를 실행하는 함수이다. 그래서 위 함수는 1000ms 뒤에 console.log('processing...')를 실행한다.
그리고 이 함수의 중요한 특성은 비동기 처리가 가능한 함수이다. 즉, setTimeout에 대한 실행은 1000ms뒤에 되고 console.log('end')의 실행이 먼저 된다. 그래서 end와 processing...의 순서가 바뀌어서 출력이 되고, end가 출력된 이후 1000ms 후에 processing...가 출력된다.

그런데 이러한 처리 방식으로 인해 우리가 바라지 않는 결과를 받을 수 있다. 왜냐하면 우리는 start -> prossing... -> end의 순서대로 결과를 받기를 원하기 때문이다. 이러한 문제를 해결하기 위해 콜백함수를 사용할 수 있다.

콜백함수로 비동기 처리

아래의 코드를 살펴보자. Calculator라는 클래스에 더하기, 빼기, 곱하기, 나누기 메서드를 만들었다. 그리고 비동기 처리를 할 수 있도록 각 메서드에 콜백함수를 이용하였다.

class Calculator {
    plus(a,b,callbackFunction){
        setTimeout(()=>{
            const result = a + b;
            callbackFunction(result); 
        },1000);
    };

    minus(a,b,callbackFunction){
        setTimeout(()=>{
            if( a >= b){
                const result = a - b;
                callbackFunction(result);
            } else {
                console.log(`can't minus`);
            }
        },1000)
    }
  
    multiply(a,b,callbackFunction){
        setTimeout(()=>{
            const result = a * b;
            callbackFunction(result);
        },1000);
    }

    divide(a,b,callbackFunction){
        setTimeout(()=>{
            if( a % b === 0){
                const result = a / b;
                callbackFunction(result);
            } else {
                console.log(`can't divide`);
            }
        },1000)
    }
}

const calculator =  new Calculator();

calculator.plus(3,4,(res1) => 
    calculator.minus(res1,2,(res2) => 
        calculator.multiply(res2, 3, (res3) => 
            calculator.divide(res3, 5, (res4 )=> 
                console.log(res4)))))

위 메서드 중 plus를 살펴보면, 인자를 a,b 이외에 callbackFunction이라는 콜백함수를 인자로 받았다. 이를 통해 a와 b를 더한 값을 result라는 변수에 할당하고, result를 인자로 받아 콜백함수를 호출한다. 이를 통해 우리가 원하는 계산 이후에 원하는 함수를 순차적으로 실행할 수 있게 되어 비동기 처리를 동기적으로 처리할 수 있게 되었다.

하지만 위 코드의 마지막 호출 예시처럼 콜백 지옥이 펼쳐져 가독성이 급격히 떨어지게 되었다. 이러한 문제를 Promise를 통해 해결할 수 있다.

Promise

Promise는 자바스크립트에서 비동기 처리를 할 때 사용되는 객체이다. 일단 Promise를 이용하여 위의 계산기 예시를 변경한 코드를 살펴보자.

class Calculator {
    plus(a,b){
        return new Promise((resolve, reject) =>{
            setTimeout(()=>{
                const result = a + b;
                resolve(result); 
            },1000);
        })
    };

    minus(a,b){
        return new Promise((resolve, reject) =>{
            if(a >= b){
                setTimeout(()=>{
                    const result = a - b;
                    resolve(result); 
                },1000);
            } else {
                reject(new Error(`can't minus`))
            }
            
        })
    }

    multiply(a,b){
        return new Promise((resolve, reject) =>{
            setTimeout(()=>{
                const result = a * b;
                resolve(result); 
            },1000);
        })
    }

    divide(a,b){
        return new Promise((resolve, reject) =>{
            if(a % b === 0){
                setTimeout(()=>{
                    const result = a / b;
                    resolve(result); 
                },1000);
            } else {
                reject(new Error(`can't divide`))
            }
        })
    }
}
const calculator = new Calculator()

calculator.plus(3,4)
    .then(res1 => calculator.minus(res1,2))
	.then(res2 => calculator.multiply(res2, 3))
    .then(res3 => calculator.divide(res3,5))
    .then(res4 => console.log(res4));

콜백의 예시에서는 메서드에 콜백함수를 인자로 받았지만 Promise의 예시에서는 콜백함수를 인자로 받지 않는다. 그리고 Promise객체를 리턴하고 있다. 더 자세히 알기 위해서는 Promise의 상태에 대해서 알아야 한다.

Promise의 상태

Promise는 크게 Pending, Fulfilled, Rejected 3가지 상태가 있다. 이는 각각 비동기 처리 로직이 아직 완료되지 않은 상태, 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태, 비동기 처리가 실패하거나 오류가 발생한 상태를 의미한다.

  • Pending
    Pending은 비동기 처리 로직이 아직 완료되지 않은 상태이다.
new Promise();

위와 같이 호출하면 Pending상태가 된다. 그리고 new Promise()호출 시, 콜백함수를 선언할 수 있고 그 콜백함수는 resolve, reject라는 두개의 인자를 갖는다.

new Promise((resolve, reject) => {
	// ...
});
  • Fulfilled
    콜백함수의 인자인 resolve를 실행하면 Promise객체는 Fulfilled 상태가 된다.
new Promise((resolve, reject) => {
	resolve();
});
  • Rejected
    콜백함수의 인자인 reject를 실행하면 Promise객체는 Rejected 상태가 된다.
new Promise((resolve, reject) => {
	reject();
});

위 그림의 흐름을 따라가 보면 처음 Promise 객체는 pending상태였다가 콜백함수를 실행하였을 때, 요청 성공과 실패에 따라 fulfilled, rejected 상태가 된다. 그리고 이것을 각각 then()이나 catch()로 fulfilled, rejected에서 처리된 값을 받을 수 있다.

Promise로 연결하기

calculator.plus(3,4)
    .then(res1 => calculator.minus(res1,2))
	.then(res2 => calculator.multiply(res2, 3))
    .then(res3 => calculator.divide(res3,5))
    .then(res4 => console.log(res4));

Promsie를 이용한 예시에서 마지막에 호출을 할 때, 다음과 같이 여러 개의 Promsie를 연결해 주면 이전에 겪었던 콜백 지옥에서 벗어날 수 있다.

출처 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise
https://joshua1988.github.io/web-development/javascript/promise-for-beginners/

profile
우물에서 자라나는 중

0개의 댓글