[JavaScript] 기다리는 자에게 복이 있나니, Promise

Dongjun Kim·2022년 12월 6일
1

⚠️JavaScript

목록 보기
3/4
post-thumbnail

자바스크립트 시리즈:

[JavaScript] 🛒JSON, 어디까지 알아보고 오셨어요?
[JavaScript] 동기적인(Synchronous) 콜백(callback)함수도 있을까?
[JavaScript] 기다리는 자에게 복이 있나니, Promise
[JavaScript] 비동기 이기는 법, Async와 Await.


저번 글 요약:

  1. Callback function은 Synchronous할 수도, Asynchronous할 수도 있는데 후자의 경우가 문제가 된다.
  2. Asynchronous한 callback function을 순차적으로 쓰고 싶으면 nested callback을 쓰면 된다.
  3. 하지만 이 방법은 Callback Hell(콜백 지옥)을 만들 수 있다.
  4. Callback은 흐름을 제어하기 어렵다는 문제가 있다.
  5. 또, 제어를 다른 함수에게 넘겨주면서 생기는 문제도 있었다.

Promise, 뭐에 쓰는 거야?

다음과 같이 할 수 있게 해준다.

const a = promiseFunction()

a.then(()=>console.log("Do Something"));

promiseFunctionpromise를 return하는 어떤 함수다.


그니까 asyncrhonous한 함수가 promise를 return하게 만들면, .then 뒤에 그 함수가 끝나면 실행할 코드를 넣을 수 있다. 순차적으로 실행할 수 있게 됐다!


순차적으로 실행해야하는 코드가 많으면 어떨까? Callback지옥같은 일이 또 생기는 건 아닐까..?

const a = promiseFunction()

a.then(()=>console.log("first"))
.then(()=>console.log("second"))
.then(()=>console.log("third"))
.then(()=>console.log("fourth"))

훨씬 깔끔해졌다!


Promise가 정확히 뭘까?

https://cdn.imweb.me/thumbnail/20180419/5ad82eea941ac.gif

카페나 음식점에 가서 키오스크에서 주문을 하면, 돈을 받고 번호표가 나온다. 실제로 음식이 나온 건 아니지만, 그렇다고 음식이 완성되서 나올때까지 다음 주문을 기다리지는 않는다. 나중에 음식이 나오게 되면 번호표를 건내주면 음식을 받을 수 있다.

Promise는 번호표 같은 역할을 한다.

또 한 가지 특징이 있다. 프로미스는 synchronous한 내용도 asynchronous하게 실행한다. 따라서 고민할 필요가 없다. 이 부분은 뒤의 프로미스의 장점에서 자세히 보자.


그래서 어떻게 만드는데?

asyncrhonous한 함수가 promise를 return하게 만들면” 이라고 했는데, 어떻게 그렇게 만들 수 있을까?

2단계로 나눌 수 있다.

  1. Promise를 만든다.
  2. 함수가 Promise를 return하게 만든다.

일단 첫 번째 단계부터 해결하자.

new Promise((resolve, reject) => {
//asynchronous한 무언가를 한다.
})
.then(
//그 다음에 무언가를 한다.
)

Promise키워드 앞에 new를 붙여서 만들면 된다!

근데 그 옆에 있는resolvereject는 뭐하는 아이들일까?


promise 실행 과정

https://www.freecodecamp.org/news/content/images/2020/06/Ekran-Resmi-2020-06-06-12.21.27.png

promise가 resolve될 경우에 아까 봤던 .then()을, reject될 경우에는 .catch()를 실행시켜준다.

이게 뭔 소릴까?

그럼 언제 resolve되고 언제 reject된다는 걸까?

답은 우리가 정해줄 때다.


코드로 나타내면 다음과 같다.

new Promise((resolve, reject)=>{
	//async한 무언가

	if(//잘 해결되었다고 믿을만한 조건){
		resolve(//return하고 싶은 것)
	}
	else{
		reject(//return하고 싶은 것)
	}
})
.then(//resolve에서 return한 값으로 무언가를 한다.)
.catch(//reject에서 return한 값으로 무언가를 한다.)

new Promise로 만든 이 함수도 무언가 실행의 결과가 있을 것이다. 그걸 resolvereject안에 적어준다.

.then이나 .catch에서 이 값을 받아서 그 다음에 실행하고 싶은 코드를 실행해주면 된다.


Promise를 return하는 함수

2번째 단계였던, 우리의 함수가 Promise를 return하게 만드는 건 간단하다.

function ourFunction(arg1, arg2){
    const workDone = someAsyncWork(arg1, arg2); //이 부분이 asynchronous한 작업이라고 가정하자.

    return new Promise((resolve, reject)=>{
        if(잘해결됐다면){resolve(workDone);}
        else{reject('무언가 잘못됐어!')}
    })
}

비동기 처리를 하고 Promise를 만들어서 return하면 된다!

근데 Promise를 return한다는 게 무슨 뜻일까?

위의 ourFunction을 불러보자.

ourFunction(someArg1, someArg2)
.then((result) => console.log(`Result: ${result}`))
.catch((error) => console.log (`Error/: ${error}`))

ourFunction은 result나 error를 반환하는 게 아니다.
그것들을 반환하겠다는 promise, 약속을 반환한다.


.then()은 어떻게 체인으로 쓸 수 있는 걸까?

그건 바로 .then()도 새로운 promise를 반환하기 때문이다. .then() 뒤의 .then()은 바로 그 새로운 promise에 대한 .then()이다.


.finally와 Promise.all, Promise.race

promise를 더 편하게 쓸 수 있게 해주는 기능들도 있다.

그 중 하나는 resolve가 되든, reject가 되든 실행되는 finally다. 공통된 부분을 담아둘 수 있어 코드 반복을 피할 수 있다.

new Promise((resolve, reject) => {
//resolve, reject
 }))
.then(() => { console.log("success") })
.catch(() => { console.log("fail") })
.finally(res => { console.log("finally") });

또, 여러 promise를 순차적으로가 아닌 동시에 쓰고 싶을 때도 있을 거다.

asynchronous한 코드를 여러 개 작동시키는데, 전부 다 끝나는 시점에 무언가를 하고 싶다면

Promise.all([promise1, promise2]).then(function(results) {
	// Both promises resolved
})
.catch(function(error) {
	// One or more promises was rejected
});

배열에 넣어서 Promise.all로 거대한 promise를 만들어주자!


마지막으로 여러 promise 중 가장 빨리 끝나는 결과를 받고 싶다면 Promise.race를 쓸 수 있다. 여기서는 Promise의 배열에서 하나의 Promise만 resolve되면 .race로 묶은 커다란 Promise도 resolve된다.


Promise가 Callback보다 나은 점들

Callback에는 크게 2가지 문제점이 있었다.

”흐름을 제어하기가 어렵다: 알아보기 어렵고, 에러 처리에도 문제가 있다”.

“뿐만 아니라, 함수의 주도권이 넘어가기 때문에 무슨 짓을 할 지 모른다.”

Callback에서는 흐름을 제어하기 어렵다.

일단 Promise는 .then() 또는 .catch()로 Promise가 해결된(Resolved) 시점을 받아온다. 따라서 이미 받아온 시점을 가정하고 걱정없이 코드를 작성할 수 있다.

다시 말해, asynchronous한 함수의 언제 끝날지 모른다는 점을 캡슐화했다. 그렇기 때문에 알아보기도, 작성하기도 더 편하다.

또 어떤 부분이 async한지, 어떤 부분이 sync한지 알 수 없는 문제점이 있었다.

Promise는 모든 것을 async하게 만들어버리기 때문에 그런 고민을 할 필요가 없다. 이는 함수가 async할지, sync할지 모른다는 Callback의 문제점도 해결해준다.

Error Handling도 더 편하다. .catch()로 받아 올 수 있다. 심지어 여러 개의 .then()으로 Promise의 chaining이 일어나 있는 경우에도 쉽게 처리할 수 있다. 몇 번째 Promise에서 rejected됐든, 이후의 가장 가까운 .catch()를 찾아서 전달되기 때문에 마지막에만 .catch()를 붙여주면 된다.

Callback에서는 함수의 주도권이 넘아간다.

Promise는 함수가 끝나는 시점을 받아와서 그 이후의 작업을 내가 정의한다. 즉, Promise는 자기가 끝난 후 무슨 일이 일어날지 알 수 없다. 그 이후 .then()에 넣어주는 Callback Function이 자기 자신에 대한 주도권을 가지고 있다. 따라서 너무 늦게 불리거나 안불리는 문제도 일어나지 않는다.

Promise는 한 번만 resolve되고, 따라서 .then()에 넣어주는 Callback Function도 1번만 실행되기 때문에 여러 번 불리는 문제도 없다.


다음에는?

asyncawait를 이용해 비동기처리를 할 수도 있다.

다음 글에서는 그에 대해 더 알아보자.🍰


자바스크립트 시리즈:

[JavaScript] 🛒JSON, 어디까지 알아보고 오셨어요?
[JavaScript] 동기적인(Synchronous) 콜백(callback)함수도 있을까?
[JavaScript] 기다리는 자에게 복이 있나니, Promise
[JavaScript] 비동기 이기는 법, Async와 Await.


참고:

https://davidwalsh.name/promises
https://github.com/getify/You-Dont-Know-JS/tree/1st-ed
https://www.freecodecamp.org/news/javascript-es6-promises-for-beginners-resolve-reject-and-chaining-explained/

profile
중요한 것은 꺾이지 않는 마음☕️

0개의 댓글