Promise [2]
Promise
var p = new Promise(function (resolve, reject) { //이걸 호출하지 않으면 then 으로 넘어가지 않는다 //resolve(2); //성공 함수로 넘어간다 //이걸 호출하지 않으면 then 으로 넘어가지 않는다 reject(2); //실패 함수로 넘어가게 된다 }); //then() 은 Primise 를 리턴한다 p.then(function (value) { //성공함수 console.log(value); return value + 1; }, function(reject) { //실패함수 console.log(reject); return 10; } ).then(function (value) { //다음 함수들 console.log(value); return value + 1; }).then(function (value) { //다음 함수들
console.log(value); throw 100; }).catch(function (ff) { //예외 함수
console.log(ff); });
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | resolve(2); 일때의 결과 2 3 4 100 reject(2); 일때의 결과 2 10 11 100 | cs |
Promise 생성하기
Promise
오브젝트는 new
키워드와 생성자를 사용하여 만듭니다. 이 생성자는 인수로 "executor 함수"라는 함수를 사용합니다. 이 함수는 매개 변수로 두 가지 함수를 가져야 합니다. 비동기 작업이 성공적으로 완료되고 결과를 값으로 반환하면 첫 번째 함수(resolve
)가 호출됩니다. 두 번째 (reject
)는 작업이 실패 할 때 호출되며 보통 오류 객체를 반환합니다.
const myFirstPromise = new Promise((resolve, reject) => {
// do something asynchronous which eventually calls either:
//
// resolve(someValue); // fulfilled
// or
// reject("failure reason"); // rejected
});
함수가 프로미스를 사용하도록 하려면 단순히 프로미스를 반환하면됩니다.
function myAsyncFunction(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = () => resolve(xhr.responseText);
xhr.onerror = () => reject(xhr.statusText);
xhr.send();
});
}
예제
기본 예제
let myFirstPromise = new Promise((resolve, reject) =>
{
// We call resolve(...) when what we were doing asynchronously was successful, and reject(...) when it failed.
// In this example, we use setTimeout(...) to simulate async code.
// In reality, you will probably be using something like XHR or an HTML5 API.
setTimeout(function(){
resolve("Success!"); // Yay! Everything went well!
}, 3000);
});
myFirstPromise.then((successMessage) => {
// successMessage is whatever we passed in the resolve(...) function above.
// It doesn't have to be a string, but if it is only a succeed message, it probably will be.
console.log("Yay! " + successMessage);
});
결과 3초 후
"Success!"
Promise.prototype.then()
then()
메서드는 Promise
를 리턴하고 두개의 콜백 함수를 인수로 받습니다. 하나는 Promise가 성공(success)했을 때를 위한 콜백 함수이고, 다른 하나는 실패(failure)했을 때를 위한 콜백 함수입니다.
구문
p.then(onFulfilled, onRejected); p.then(function(value) { // 이행(fulfillment) }, function(reason) { // 거부 });
매개변수
onFulfilled
Promise
가 수행될 때 호출되는Function
이고, 수행 값(fulfillment value) 하나를 인수로 받습니다.onRejected
Promise
가 거부될 때 호출되는Function
이고, 거부 이유(rejection reason) 하나를 인수로 받습니다.
설명
then
과 Promise.prototype.catch()
메서드는 promise 를 리턴하기 때문에, chaining 이 가능합니다. — composition 이라고도 합니다.
예
then
메서드 사용
var p1 = new Promise(function(resolve, reject) {
resolve("Success!");
// 또는
// reject ("Error!");
});
p1.then(function(value) {
console.log(value); // 성공!
}, function(reason) {
console.log(reason); // 오류!
});
Chaining
then
메서드는 Promise
를 리턴하기 때문에, 이어지는 then
호출들을 손쉽게 chaining 할 수 있습니다. onFulfilled
또는 onRejected
콜백 함수가 리턴하는 값은 자동으로 resolved promise로 wrapping 되기 때문에 다음에 오는 then 이나 catch 메서드로 전달 됩니다.
var p2 = new Promise(function(resolve, reject) {
resolve(1);
});
p2.then(function(value) {
console.log(value); // 1
return value + 1;
}).then(function(value) {
console.log(value); // 2
});
p2.then(function(value) {
console.log(value); // 1
});
다음과 같이 catch()
를 사용할 수도 있습니다.
get('story.json').then(function(response) {
console.log("Success!", response);
}).catch(function(error) {
console.log("Failed!", error);
})
catch()
에 대한 특별한 것은 없습니다. then(undefined, func)
의 보완에 불과하지만 가독성은 훨씬 높습니다. 상기 두 코드 예시는 동일하게 동작하지 않습니다. 후자는 다음과 같습니다.
get('story.json').then(function(response) {
console.log("Success!", response);
}).then(undefined, function(error) {
console.log("Failed!", error);
})
차이는 미묘하지만 매우 유용합니다. 프라미스 거부는 거부 콜백(또는 동일하게 기능하는 catch()
)을 사용하여 다음 then()
으로 건너뜁니다. then(func1, func2)
를 사용하는 경우 func1
와 func2
중에 하나만 호출되며, 둘이 동시에 호출되지 않습니다. 그러나 then(func1).catch(func2)
를 사용하는 경우 둘은 체인에서 개별적인 단계이므로 func1
이 거부하면 둘 다 호출됩니다. 다음을 봅시다.
asyncThing1().then(function() {
return asyncThing2();
}).then(function() {
return asyncThing3();
}).catch(function(err) {
return asyncRecovery1();
}).then(function() {
return asyncThing4();
}, function(err) {
return asyncRecovery2();
}).catch(function(err) {
console.log("Don't worry about it");
}).then(function() {
console.log("All done!");
})
위의 흐름은 일반 자바스크립트 try/catch와 매우 유사하며, 'try'를 사용하여 발생하는 오류는 즉시 catch()
블록으로 이동합니다. 다음은 이를 흐름도로 만든 것입니다.
프라미스 처리 시에는 청색선을 따르고 프라미스 거부 시에는 적색선을 따르면 됩니다.
new Promise, Promise.resolve() 차이
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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | //new Promise 패턴을 사용하면 아래와 같이 쓸 수 있습니다. function async1 (param) { return new Promise(function(resolve, reject) { resolve(param*2); }); } function async2 (param) { return new Promise(function(resolve, reject) { resolve(param*3); }); } function async3 (param) { return new Promise(function(resolve, reject) { resolve(param*4); }); } var start = 1; async1(start) .then(async2) .then(async3) .then(result => { console.log(result); // 24 }); //함수 선언 코드가 좀 길어지긴 했지만... 함수 사용 부분은 좀 더 명확해졌습니다. //같은 내용을 Promise.resolve() 로 사용하면 아래와 같죠. function async1 (param) { return Promise.resolve(param*2); } function async2 (param) { return Promise.resolve(param*3); } function async3 (param) { return Promise.resolve(param*4); } var start = 1; async1(start) .then(async2) .then(async3) .then(result => { console.log(result); // 24 }); //그런데 이런 상황이 존재함 //(이 예제는 크롬의 cross origin 제약을 해제하고 실행했습니다.) function request (param) { return Promise.resolve() .then(function () { var xhr = new XMLHttpRequest(); xhr.open('GET', 'http://google.co.kr/', true); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { return Promise.resolve(xhr.response); } }; xhr.send(null); }); } Promise.resolve() .then(request) .then(result => { console.log('ok'); console.log(result); }); /* 위 코드는 http://google.co.kr에 GET 요청을 보낸 뒤 받아서 결과를 텍스트로 출력하는 예제입니다. Promise 안에서 readyState와 status를 확인해서 Promise.resolve()를 리턴하므로 얼핏 보면 돌아갈 것 같습니다만... 제대로 동작하지 않습니다. 콘솔에 찍히는 result는 undefined 입니다. */ 결과는 ok undefined //request() 함수에서 xhr.send() 를 실행하면 해당 컨텍스트에서의 작업은 끝나고 Promise 컨텍스트도 종료됩니다. //xhr이 완료되었을 때 반환하는 객체를 받을 타이밍에는 이미 이전 함수가 끝나있기 때문이죠. //이 코드는 new Promise()를 써서 이렇게 바뀌어야 합니다. function request (param) { return new Promise(function(resolve, reject) { var xhr = new XMLHttpRequest(); xhr.open('GET', 'http://google.co.kr/', true); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { resolve(xhr.response); } }; xhr.send(null); }) } Promise.resolve() .then(request) .then(result => { console.log('ok'); console.log(result); }); /* 두번째 라인에서 생성한 Promise 객체의 함수 실행 컨텍스트를 유지하기 위해 Promise.resolve() 대신 new Promise()를 사용하고 최종 콜백 지점인 여덟번째 라인에서 resolve()를 실행해줍니다. 기존의 callback 사용 방식과 비슷하다고 보면 간단합니다. 이렇게 사용하면 Promise 실행 컨텍스트를 벗어나지 않고 원하는 결과를 얻을 수 있습니다.*/ | cs |
비슷하게 ajax 콜을 요청하거나 DB에서 데이터를 가져올 때와 같이 async 함수를 감싸는 경우는 new Promise()를
사용하면 됩니다.
처음 Promise를 사용할 때는 왜 같은 흐름의 함수가 두 개로 제공될까 이상했는데, 사용하다보니
return Promise.resolve()와 new Promise()는 사용 방법이 약간 다릅니다. return Promise.resolve()는
sync 로직 흐름에서, new Promise()는 sync는 물론, async 로직 흐름에서도 사용할 수 있습니다.
요새 모듈은 사용할 때 Promise를 지원하는 경우가 종종 있습니다. 좀 더 많은 모듈이 Promise를 지원하기를 바래봅니다
ref : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise
ref : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
ref : https://developers.google.com/web/fundamentals/primers/promises
ref : http://han41858.tistory.com/11