이 글은 Javascript의 비동기 프로그래밍의 기초적인 이해를 마치고, 비동기 프로그래밍을 위한 Promise 문법에 대해 정리한 글이다. 이 글의 이해를 위해서는 먼저 아래 링크의 글을 완전히 이해하고 있어야 한다.
Javascript / 기초 / 비동기(Asynchronous), 콜백(callback), setTimeout
1. Promise
callback 함수의 중첩(callback hell)을 피하기 위해 사용하기 편한 Promise 문법에 대해 정리한다. Promise는 추후 다룰 client-server간 비동기 처리의 핵심 내용이므로 잘 이해하여야 한다.
각 함수의 실행에 걸리는 시간과 상관없이 내가 원하는 순서대로 함수들을 실행하기 위해서는 callback 함수의 중첩이 필요했다. 이것을 Promise 문법과 비교하여 작성하면 아래와 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
// 1. Callback 함수 중첩
let printer = function (letters, callback) {
setTimeout(
() => {
console.log(letters)
callback()
},
Math.floor(Math.random() * 100) + 1
)
}
let allPrinter2 = function() {
printer("a", () => {
printer("b", () => {
printer("c", () => {} )
})
})
}
//실행의 성공, 실패에 대한 err, data 처리 구문은 따로 작성해주어야 함.
allPrinter2()
// 2. Promise 문법 사용
let printer = function (letters) {
return new Promise((resolve, reject) => {
setTimeout(
() => {
console.log(letters)
resolve()
reject(new Error("Request is failed")) },
Math.floor(Math.random() * 100) + 1
)
})
}
let allPrinter3 = function() {
printer("a")
.then(() => {
return printer("b")
})
.then(() => {
return printer("c")
})
.catch((err) => {
console.log(err);
})
} allPrinter3()
|
cs |
생소한 구문이 많지만, 차근차근 차이점을 살펴보면 크게 어렵지는 않다.
1. 비동기 함수를 원하는 순서대로 실행할 함수(printer) 안에 Promise 객체를 생성한다(new Promise).
그 객체를 return 한다.
Promise라는 객체를 만든다는 것은 callback을 이용하여 비동기 함수를 원하는 순서대로 처리하는 기능을 넣겠다는 것이다.
즉, 어떤 함수를 여러 개 실행하는데 "1. 한 개 함수를 실행하고, 그 실행이 완료되고 나면 다음 함수를 처리하겠다"는 개념, "2. 내가 원하는 순서대로 함수들을 실행하겠다(.then)."는 개념과 "3. 실행 성공, 실패 시 어떤 처리를 할지를 넣어주겠다(resolve, reject)." 는 기능을 포함시키겠다는 것이다.
2. Promise 객체를 생성하는 코드 안에, resolve와 reject 함수를 실행한다.
Promise 객체는 resolve, reject를 인자로 받아오고 함수 내부에서 실행한다는 점에서 이 둘은 callback 함수이다. Promise 객체의 문법 정의에 따라서, resolve는 내부 비동기 함수(setTimeout)를 정상적으로 실행완료 처리하겠다는 것이고, reject는 비동기 함수를 실패처리 하겠다는 것이 된다. setTimeout은 server와 통신 등을 하는 것이 아니라 그저 설정된 시간 이후에 실행이 완료되는 code이므로 resolve에만 걸려서 모두 다 정상작동하게 된다. 만약 해당 code가 setTimeout이 아니라, server와 통신하는 code이라면, 통신 성공 시 resolve를 실행하여 Promise의 상태를 fulfilled로 바꿔주고, 통신 실패 시 reject를 실행하여 Promise의 상태를 rejected로 바꿔주는 것이다. resolve와 reject의 () 안에 인자를 써주면, 각 상태 처리 뿐만 아니라 PromiseResult 값을 해당 파라미터값으로 지정해주게 된다. Promise statuss(상태) 및 PromiseResult 값에 대한 내용은 아래 내용에 기술하였다.
3. Promise의 3가지 상태(대기, 실행완료 및 실패처리)
Promise 객체는 3가지 상태(States)를 갖는다.
- pending(대기) : new Promise()로 Promise 객체를 생성하면 지정되는 상태이다.
- fulfilled(이행) : 콜백 함수 resolve를 실행하면 지정되는 상태이다.
- rejected(실패) : 콜백 함수 reject를 실행하면 지정되는 상태이다.
위의 코드에서 setTimeout 함수는 임의의 시간 뒤에 실행된다. 해당 시간 뒤에 console.log()를 실행하고 resolve() 처리를 하도록 되어있다. 원래는 callback()을 실행하여, 실제 실행구문에서 "printer("a", () => { ... })"으로 실행되는 부분이다. Promise에서는 resolve()로 완료 처리된 Promise 객체를 return 하고, 그 객체를 .then으로 받아오게 된다.
4. Promise의 정의 및 상태처리 후, 실제 실행 구문에서 .then, .catch를 사용하였다.
printer("a")를 통해 한 개의 Promise 객체가 완료(resolve)된 상태로 만들고, 그 객체에 .then을 추가해주었다.
.then 구문은 "앞에서 받아오는 Promise 객체를 resolve된 상태로 받아오고 나서, 그리고..." 라는 의미를 갖는다.
반드시 Promise 객체를 받아와야 한다. 상기 예제에서는 .then의 파라미터값이 없지만, 만약 파라미터 값을 지정해줄 경우 .then의 앞에서 받아온 'Promise의 값'을 해당 파라미터로 지정해주게 된다.
다음의 예시 코드를 보자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
const adder = (wait) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if(wait > 500) {
resolve(1)
} else {
reject(new Error('에러'))
}
}, wait);
})
.then((data) => {
return data + 2
})
.catch((err) => {
return err
})
}
|
cs |
wait 값이 500보다 크면, Promise가 완료처리 되며 Promise의 값으로 1을 갖는다. 그 외의 경우, Error 객체를 만들고 그 값으로 '에러' 라는 문자열을 갖는다.
.then에서 앞서 처리된 Promise의 값인 1을 'data'라는 파라미터로 선정하고, 거기에 2를 더하여 결과값을 return 한다.
만약 wait가 500보다 작거나 같아서 reject에 걸리게 되면, reject로 생성된 Error 객체가 Promise 객체에 담기게 되고,
.catch 구문에 걸리게 되어 Error 객체가 'err' 라는 파라미터로 전달되고 return 된다. 즉 '에러'라는 Promise 값을 갖는 Promise가 리턴된다.
adder 함수를 각기 다른 wait 값에 따라 호출했을 때의 결과이다. 모두 Promise 객체를 리턴함을 알 수 있다.
Promise 객체가 리턴된 후, 자세한 값을 알기 위해 "Promise" 앞에 있는 화살표를 지정한 wait(milisecond) 시간 전에 확인하면 resolve됬음에도 불구하고 pending이 떠버린다. 주의하여 값을 확인해야한다.
2. Promise Chain, Promise.all, Promise Hell
위에서 Promise의 기본 개념을 파악했다면, 아래 개념들은 쉽게 알 수 있는 내용들이다.
2-1. Promise chain
Promise의 결과값들이 .then과 .catch를 통해 chain처럼 연결되어 있다는 의미이다. 맨 처음 실행한 비동기 함수의 결과값인 Promise 객체를 가지고, 다음 함수를 실행한다. 계속해서 성공 시 .then으로 넘어가고, 한번이라도 실패하면 .catch로 넘어가게 되어 err값을 리턴하게 된다. 아래 그림을 참조하자.
2-2. Promise.all
Promise.all은 Array와 같이 iterable한 객체를 파라미터로 받는다. 여러 개의 promise를 한번에 처리하여 결과를 보고자 할 때 유용하다. promise.all 안에 있는 배열의 요청들은 동시에 실행된다.(promise chaining은 하나씩 실행 시킨다.)
그리고 모든 promise가 전부다 성공해야 then, 하나라도 실패하면 catch로 처리한다.
Promise.all의 파라미터로 받는 객체에 꼭 promise값이 아닌 값이 담겨있어도 실행이 된다.
(참조 : MDN Promise.all - developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/all )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
const adder = (wait) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if(wait > 500) {
resolve(1)
} else {
reject(new Error('에러'))
}
}, wait);
})
.then((data) => {
return Promise.all([data + 2, data + 3, data + 4, 10])
})
.catch((err) => {
return err
})
}
//결과 : Promise. PromiseResult : [3, 4, 5, 10]
|
cs |
2-3. Promise hell
callback hell과 마찬가지로, Promise chaining의 수가 많아지면 가독성이 떨어지는 문제가 발생할 수 있다. 다음 글에서 Async/await 문법을 통해 비동기 프로그래밍을 조금 더 개선해본다.
다음글
Javascript / 기초 / 비동기 프로그래밍(Asynchronous) : Async/await
참조
1) 캡틴 판교 - 자바스크립트 Promise 쉽게 이해하기
joshua1988.github.io/web-development/javascript/promise-for-beginners/
2) MDN - Promise
developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise
developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise
developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
'Programming-[Frontend] > Javascript' 카테고리의 다른 글
[요약] v8 엔진, 자바스크립트의 작동원리 (0) | 2020.12.30 |
---|---|
Javascript / 기초 / 비동기 프로그래밍(Asynchronous) : Async/await (0) | 2020.09.27 |
Javascript / 기초 / 비동기(Asynchronous), 콜백(callback), setTimeout (0) | 2020.09.21 |
Javascript / Tips / MDN 문서에서 대괄호 [ ] (brackets)의 의미 (0) | 2020.09.18 |
Javascript / 기초 / Inheritance, Object - oriented (상속, 객체지향) (0) | 2020.09.09 |