본문 바로가기
카테고리 없음

비동기 프로그래밍 - new Promise

by cariño 2022. 10. 16.
728x90
반응형

Promise

promise 객체는 아래와 같은 문법으로 만든다.

let promise = new Promise(function(resolve, reject){
    // executor
});

 

프라미스는 성공 또는 실패만 한다.

new Promise

state: pending

result: undefined

 

[resolve]
state: fullfilled
result: value

 

[reject]
state: rejected
result: error
 
executor는 promise의 상태를 둘 중 하나로 변화시킨다. 
  • resolve(value) — 일이 성공적으로 끝난 경우 그 결과를 나타내는 value와 함께 호출
  • reject(error) — 에러 발생 시 에러 객체를 나타내는 error와 함께 호출
const pr = new Promise((resolve, reject) => {
    // 어떤 일이 완료된 후 실행 되는 함수 : callback

    setTimeout(() => {
        resolve('ok') //fullfilled
        reject(new Error('error..')) //rejected
    }, 3000)
})

 

[resolve·reject 함수 즉시 호출하기]

 

executor는 대개 무언가를 비동기적으로 수행하고, 약간의 시간이 지난 후에 resolve, reject를 호출하는데, 꼭 이렇게 할 필요는 없습니다. 아래와 같이 resolve reject를 즉시 호출할 수도 있다.
let promise = new Promise(function(resolve, reject) {
  // 일을 끝마치는 데 시간이 들지 않음
  resolve(123); // 결과(123)를 즉시 resolve에 전달함
});
 

 

then, catch, finally

 

promise 객체의 state, result 프로퍼티는 내부에 있다.

then, catch, finally를 사용하면 접근이 가능하다.

 

 

[then]

.then은 프라미스에서 가장 중요하고 기본이 되는 메서드이다.

.then의 첫 번째 인수는 프라미스가 이행되었을 때 실행되는 함수이고, 여기서 실행 결과를 받는다.

.then의 두 번째 인수는 프라미스가 거부되었을 때 실행되는 함수이고, 여기서 에러를 받는다.

작업이 성공적으로 처리된 경우만 다루고 싶다면 .then에 인수를 하나만 전달하면 된다.

//then을 이용해서 pr의 인수로 reject와 resolve를 처리할 수 있다. 
pr.then(
    function (result) { } - 이행되었을 때
    function (err) { } - 거부되었을 때
)​

 

[catch]

에러가 발생한 경우만 다루고 싶다면 catch

catch .then null을 전달하는 것과 동일하게 작동한다.

 

[fianlly]

try {...} catch {...}에 finally 절이 있는 것처럼, 프라미스에도 finally가 있다.

쓸모가 없어진 로딩 인디케이터(loading indicator)를 멈추는 경우같이, 결과가 어떻든 마무리가 필요하면 finally가 유용하다.

▼ //then과 catch, finally를 사용할 수 있다.

pr.then(
    function (result) { }
).catch( //가독성 편으로 좋다.
    function (error) { }
).finally(// reject, resolve 처리가 완료되면 항상 실행 됨 
    function () {
        console.log('---종료---')
    }
)

[비동기 처리를 위한 콜백 패턴의 단점]


왜 promise를 쓰게됐는지, callback hell의 모습을 살펴보자.

// callback hell
const f1 = (callback) => {
    setTimeout(() => {
        res('1번 주문 완료')
        callback()
    }, 1000);
}

const f2 = (callback) => {
    setTimeout(() => {
        res('2번 주문 완료')
        callback()
    }, 1000);
}

const f3 = (callback) => {
    setTimeout(() => {
        res('3번 주문 완료')
        callback()
    }, 1000);
}

console.log('시작')
f1(function () {
    f2(function () {
        f3(function () {
            console.log('종료')
        })
    })
})

 

[Promise chainng]

해당 콜백 헬 로직을 Promise 객체로 바꿔보기

//promise로 구현

const f1 = () => {
    return new Promise((res, rej) => {
        setTimeout(() => {
            res('1번 주문 완료')
        }, 1000);
    })
}

const f2 = (message) => {
    console.log(message)
    return new Promise((res, rej) => {
        setTimeout(() => {
            res('2번 주문 완료')
        }, 1000);
    })
}

const f3 = (message) => {
    console.log(message)
    return new Promise((res, rej) => {
        setTimeout(() => {
            res('3번 주문 완료')
        }, 1000);
    })
}

console.log('시작')
f1()
    .then(res => f2(res))
    .then(res => f3(res))
    .then(res => console.log(res))
    .catch(console.log)
    .finally(() => {
        console.log('끝')
    })

Promise.all

 

모두 한꺼번에 이행시킬 수 있다.

프라미스가 이행될 때까지 기다리다 그 결과 값을 담은 배열을 반환하는 메서드이다.

여러개의 프로미스가 모두 resolve됐을 때 바로 다음 로직을 실행하는 경우에 사용한다.

promise.all()을 사용할 때 주의해야할 점은 배열 내 요소 중 어느 하나라도 거부하면 즉시 거부한다.

 

* 복수의 URL에 request를 보내고, 모든 요청의 응답이 올때 화면을 랜더 해야하는 상황이 그 예시이다.

요청에 필요한 정보를 배열로 저장한 뒤, 그후 해당 정보를 프로미스로 매핑하여 Promise.all()에 입력하는

방법이 자주 쓰인다.

// promise all
console.time('x')
Promise.all([f1(), f2(), f3()]) //세 작업이 모두 완료되어야지만 then부분이 실행됨
    .then(res => {
        console.log(res)
        console.timeEnd('x')
    })

 

Promise.race

 

Promise.race()는 가장 빨리 응답을 받은 결과값만 resolve한다.

가장 빨리 도착한 promise의 result만이 .then() 구문으로 넘어갈 수 있다.

// promise.race

console.time('x')
Promise.race([f1(), f2(), f3()])
    .then(res => {
        console.log(res)
        console.timeEnd('x')
    })

//1번 주문 완료
x: 1.015s

// 차이점: 하나라도 1등으로 완료되면 종료시킴
728x90

댓글