Promise 패턴
비동기 작업을 순차적으로 처리하게 되면 앞의 콜백 패턴에서 본 것처럼 콜백이 중첩된 콜백 트라이앵글 구조가 되어서 코드의 가독성이 떨어지게 된다.
콜백 중첩으로 인한 코드 가독성을 해결하기 위해서 Promise 패턴이 제안되었다. 이 패턴은 코드가 병렬로 배열되어서 가독성이 높아지고 또한 오류 처리등에 대해서도 추가되었다. es5에서 Promise가 많이 사용되자 ES6에서부터는 Promise 가 표준 객체가 되었다.
가장 기초적으로 기억해야 할 것은 다음과 같다.
- Promise 패턴은 비동기 작업을 순차적으로 처리한다.
- Promise는 new 키워드로 선언과 동시에 실행된다.
- Promise는 .then() 콜백 메서드에서 비동기 작업의 결과를 처리한다.
- Promise는 반드시 resolve 되거나 reject 되어야 한다. 만일 어느한쪽도 리턴되지 않으면 .then() 이 호출되지 않는다. 비유하자면 약속을 했으면 반드시 지켜지거나 파기되어야 한다. 어느쪽도 아니면 아직 pending 상태가 되어서 .then()이 호출 되지 않는다.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
let async1 = function (param) { return new Promise((resolve, reject)=>{ if(param && param > 0) { resolve('positive'); } else if(param && param < 0) { reject('nagative'); } else if(param === 0) { throw 'error'; } else { // nothing } }); }; |
Q1: async1(1) 을 호출하면 ‘positive’ 를 출력하도록 .then()의 콜백 함수를 완성하시오.
1 |
async1(1).then(data => console.log(data)); |
첫번째 if문을 타게되고 resove(‘positive’) 를 호출하게되면 .then() 의 첫번째 파라메터의 콜백함수가 호출되게 되고 콜백 함수의 첫번째 파라메터에 resolve에서 리턴한 변수가 온다.
Q2: async1(-1) 을 호출시 ‘negative’ 가 출력되도록 .then()을 완성하시오.
1 |
async1(-1).then(data => console.log(data), error => console.log(error)); |
reject가 호출되면 .then()의 두번째 파라메터의 콜백함수가 불린다. 두번째 파라메터 대신 .catch() 를 사용해서 처리할 수도 있다.
1 |
async1(-1).then(data => console.log(data)).catch(error => console.log(error)); |
Q3: async(0) 을 호출시 에러가 리턴된다. .catch() 로 error를 출력하시오.
1 |
async1(0).then(data => console.log(data)).catch(error => console.log(error)); |
reject로 리턴했을때와 마찬가지로 .then()의 두번째 파라메터로 혹은 .catch()로 에러를 처리할 수 있다.
Q4: async(‘a’) 를 호출할 경우 무엇이 출력되는가? 그 이유는?
async(‘a’) 를 호출할 경우 else 문에 진입하게 되게 resolve, reject 어느 것도 호출하지 않는다. 약속은 반드시 지켜지거나 파기되어야 하는데 어느것도 호출되지 않으면 아직 약속이 pending 중인 상태가 되어서 .then()이 호출되지 않는다. 따라서 아무것도 출력되지 않는다.
Q5: 앞에서 살펴본 콜백패턴의 코드, 1초후마다 순차적으로 A, B, C를 출력하는 아래 코드를 promise 패턴으로 바꾸시오.
1 2 3 4 5 6 7 8 9 |
setTimeout(() => { console.log('A'); setTimeout(() => { console.log('B'); setTimeout(() => { console.log('c'); }, 1000); }, 1000); }, 1000); |
한꺼번에 하면 어려우므로 순차적으로 해보자.
먼저, 1초후에 A를 리턴하는 setTimeout 비동기 작업을 new Promise로 감싼다. new Promise로 감싸게 되면 Promise는 선언과 동시에 즉시 실행되므로 비동기 작업이 실행되고 1초후에 A가 resolve 되게 된다.
1 2 3 4 5 |
new Promise(resolve => { setTimeout(() => { resolve('A'); }, 1000); }) |
A가 resolve 되면 .then으로 결과를 받을수 있다. 먼저 A를 출력후에 다시 1초후에 B를 출력하는 비동기 작업을 new Promise로 감싼 다음에 return한다.
Promise에서는 .then()에서 return 하면 다시 .then()에서 받을수 있다. 즉, 아래와 같이 사용할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 |
new Promise(resolve => { resolve('A'); }).then(data => { console.log(data); return 'B'; }).then(data => { console.log(data); return 'C'; }).then(data => { console.log('C'); }) |
따라서, .then()에서 1초후에 B를 resolve하는 비동기 작업을 new Promise로 감싸서 리턴하면 다음과 같다.
1 2 3 4 5 6 7 8 |
.then(data => { console.log(data); return new Promise(resolve => { setTimeout(() => { resolve('B'); }, 1000) }) }) |
그러면 1초후에 B가 resolve가 되므로 .then()을 연결해서 다시 1초후에 C를 resolve하는 비동기 작업을 new Promise로 감싼후에 다시 리턴한다.
1 2 3 4 5 6 7 8 |
.then(data => { console.log(data); return new Promise(resolve => { setTimeout(() => { resolve('C'); }, 1000) }) }) |
이제는 더이상의 비동기 작업이 없으므로 리턴값을 받아서 출력만 한다.
1 |
.then(data => console.log(data)); |
다 연결하면 다음과 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
new Promise(resolve => { setTimeout(() => { resolve('A'); }, 1000); }).then(data => { console.log(data); return new Promise(resolve => { setTimeout(() => { resolve('B'); }, 1000) }) }).then(data => { console.log(data); return new Promise(resolve => { setTimeout(() => { resolve('C'); }, 1000) }) }).then(data => console.log(data)); |
Q6: 이번에는 1초후에 A, B, C를 실행하는 3가지 비동기 작업을 순차적으로 처리하는게 아니라 한꺼번에 동시에 병렬로 실행할려면 어떻게 해야하는가?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
const promiseA = new Promise(resolve => { setTimeout(function(){ resolve('A'); }, 1000); }); const promiseB = new Promise(resolve => { setTimeout(function(){ resolve('B'); }, 1000); }); const promiseC = new Promise(resolve => { setTimeout(function(){ resolve('C'); }, 1000); }); |
Promise.all을 사용해서 비동기 작업을 동시 처리를 할 수 있다. 그리고 비동기 작업이 모두 끝나면 .then()이 호출된다. 예를들어, 하나는 1초, 하나는 2초, 하나는 3초라면 동시에 시작해서 제일 시간이 긴 3초후에 .then()이 호출된다는 것이다.
출력된 결과는 [‘A’, ‘B’, ‘C’] 가 된다.
1 2 |
Promise.all([promiseA, promiseB, promiseC]) .then(values => console.log(values)) |
CodePen
See the Pen Promise pattern Quiz by LeeDongKee (@eastflag) on CodePen.0
+ reference
When you log into CodePen and fork, the project will be copied to your account. If you solve the problem and leave a codepen link in the comments below, I’ll give it a feedback