Promise
위 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다. 라고 되어 있다. 자세한건 코드를 직접 써보면서 이해를 해보자.
자세한 정보는 아래의 mdn사이트를 참조해도 좋을 것 같다.
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise
Promise는 ES6부터 JS의 표준 내장 객체로 추가되었다.
console.log(Promise) // f Promise()
위와 같이 객체로 포함되는 것을 알 수 있다.
executor 함수는 resolve와 reject를 인자로 가지게 된다. 이 둘은 함수이다! ( resolve(),reject() )
new Promise(/*executor*/(resolve,reject) => {});
위상태 가 지나면 pending 상태가 되게 된다.
new Promise(/*executor*/(resolve,reject) => {}); //pending
그리고 resolve함수를 실행하면, fulfilled(이행된) 상태로 변하게 된다.
new Promise(/*executor*/(resolve,reject) => {
//
// ...(내용)
resolve(); //fullfilled
});
reject() 함수를 실행하면 rejected(거부) 상태가 되게 된다.
new Promise(/*executor*/(resolve,reject) => {
//
// ...(내용)
reject(); //rejected
});
예를들어 객체 실행 후에 1초후에 fulfilled상태가 되는 객체를 만들어보자.
const p = new Promise((resolve,reject)=>{
/*pending*/
setTimeout(() =>{
resolve(); //fulfilled
}, 1000);
});
p.then(() =>{
console.log('1000ms 후에 fulfilled됩니다.')
});
다음과 같이 setTimeout함수를 이용하여 1초뒤에 fulfilled를 만들면 그 이후에 p.then함수가 실행되게 함을 알 수 있다. 실제로 1초 후에 출력이 된다!
실제 실무의 경우에서는 저 객체가 선언되는 순간을 명확하게 제시해줘야 한다.
function p() {
return new Promise((resolve,reject)=>{
/*pending*/
setTimeout(() =>{
resolve(); //fulfilled
}, 1000);
});
}
p().then(() =>{
console.log('1000ms 후에 fulfilled됩니다.')
})
그렇다면 reject는 어떻게 사용할까?
function p() {
return new Promise((resolve,reject)=>{
/*pending*/
setTimeout(() =>{
reject();
}, 1000);
});
}
p().then(() =>{}).catch(() =>{
console.log('1000ms 후에 rejected됩니다.')
});
조금 헷갈릴 수있는 문법이나 차근차근 보면 된다. p.then의 함수 뒤편에 .catch를 이용하여 rejected되었을 상태를 나타내게 되는 것이다.
또한 resolve에 인자를 넣어서 전달해줄 수도 있다.
function p() {
return new Promise((resolve,reject)=>{
/*pending*/
setTimeout(() =>{
resolve('mingyu');
}, 1000);
});
}
p().then((message) =>{
console.log('1000ms 후에 resolve됩니다.',message)
}).catch(() =>{
console.log('1000ms 후에 rejected됩니다.')
});
mingyu를 message로 받아서 then에서 출력해주는 모습이다.
reject도 역시 가능하다.
function p() {
return new Promise((resolve,reject)=>{
/*pending*/
setTimeout(() =>{
reject('error');
}, 1000);
});
}
p().then((message) =>{
console.log('1000ms 후에 resolve됩니다.',message)
}).catch((reason) =>{
console.log('1000ms 후에 rejected됩니다.',reason)
});
보통 JS에서는 에러 객체가 있기때문에 그를 활용하는게 일반적이다.
function p() {
return new Promise((resolve,reject)=>{
/*pending*/
setTimeout(() =>{
reject(new Error('no reason'));
}, 1000);
});
}
p().then((message) =>{
console.log('1000ms 후에 resolve됩니다.',message)
}).catch((error) =>{
console.log('1000ms 후에 rejected됩니다.',error)
});
위와같은 에러코드를 만드는데 사용되는 방법이다
만약 then함수 이후에 마지막에 어떤 작업을 수행하고 싶게 하면, finally를 사용하면 된다.
function p() {
return new Promise((resolve,reject)=>{
/*pending*/
setTimeout(() =>{
reject(new Error('no reason'));
}, 1000);
});
}
p()
.then((message) =>{
console.log('1000ms 후에 resolve됩니다.',message)
})
.catch((error) =>{
console.log('1000ms 후에 rejected됩니다.',error)
})
.finally(() =>{
console.log('end')
});
만약 비동기작업을 callback을 써서 한다면
function c(callback) {
setTimeout(() => {
callback();
}, 1000);
}
c(() => {
console.log('1000ms 후에 callback 함수가 실행');
})
c(() => {
c(() => {
console.log('2000ms 후에 callback 함수가 실행');
})
})
c(() => {
c(() => {
c(() => {
console.log('3000ms 후에 callback 함수가 실행');
})
})
})
다음과 같이 점점 안으로 들어가야 한다는 특성이 있다.
이를 promise를 이용하면 안으로 들어가는 방식이 아닌, 체인형으로 코드를 짤 수 있다.
function p() {
return new Promise((resolve,reject) =>{
setTimeout(() => {
resolve();
}, 1000);
});
}
p()
.then(() => {
console.log('1000ms 후에 fulfilled 됩니다.');
return p();
})
.then(() => p()) //arrow 특성
.then(p)
.then(() => {
console.log('4000ms 후에 fulfilled 됩니다.');
});
Promise.resolve()를 이용하면 Promise객체를 생성함과 동시에 resolve를 실행시킬 수 있다.
Promise.resolve(/* value */);
Promise.resolve(new Promise((resolve, reject) => {
setTimeout(() => {
resolve('mingyu');
}, 1000);
})).then(data => {
console.log('Promis 객체인 경우, resolve 된 결과를 받아 then이 실행', data);
});
Promise.resolve('bar').then(data =>{
console.log('then 메서드가 없는 경우, fulfilled 됩니다',data);
})
어떤 객체가 Promise객체인지 일반객체인지 모를때 Promise.resolve로 판단하는데 사용할 수 있다.
같은 맥락으로 reject도 가능하다.
Promise.reject(/*value*/);
Promise.reject(new Error('reason'))
.then(error => {
})
.catch(error => {
console.log(error);
});
물론 객체를 실행하자마자 error를 반환하는 못된 코드가 된다.
Promise.all
프로미스 객체 여러개를 생성하여, 배열로 만들어 인자로 넣고 Promise.all을 실행하면, 배열의 모든 프로미스 객체들이 fulfilled 되었을 경우 then의 함수가 실행되게 된다. then의 함수의 인자로 프로미스 객체들의 resolve 인자값을 배열로 돌려준다.
function p(ms){
return new Promise((resolve,reject)=>{
setTimeout(() => {
resolve(ms);
}, ms);
})
}
Promise.all([p(1000),p(2000),p(3000)]).then((message) => {
console.log('모두 fulfilled된 이후 실행',message);
})
즉, 비동기 실행이 모두 완료된 다음에 실행하길 원할 때 사용할 수 있다.
Promise.race
프로미스 객체들 중에서 가장 먼저 fulfilled된 것으로 then의 함수가 실행된다. then의 인자로 가장 먼저 fulfilled된 프로미스 객체의 resolve 인자값을 돌려준다.
function p(ms){
return new Promise((resolve,reject)=>{
setTimeout(() => {
resolve(ms);
}, ms);
})
}
Promise.race([p(1000),p(2000),p(3000)]).then((message) => {
console.log('가장먼저 fulfilled된 이후 실행',message);
}) //가장먼저 fulfilled된 이후 실행 1000
전자과에서 배우는 SoC(System on Chip)을 배우면 이러한 비동기식 코드에 대해 다루게 되는데 그 과정과 유사한 것 같다.
Async-Await
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/async_function
async function 함수이름() {}
cont 함수이름 = async () =>{}
처럼 함수를 선언하듯이 선언하면 된다.
await
이 함수의 가장 큰 장점은 비동기 실행인데 밑으로 흘러가듯이 코드를 진행시키게 할 수 있는 것이다. 단, 함수 동작은 꼭 async function 안에서 진행되어야 한다.
function p(ms) {
return new Promise((resolve,reject) =>{
setTimeout(() => {
resolve(ms);
}, ms);
})
}
async function main() { //awit는 항상 async function안에 있어야 한다.
const ms = await p(1000); //p함수가 다실행되서 resolve가 실행된 다음에 ms에 인자를 넣어주게 된다.
console.log(`${ms}ms 후에 실행`);
}
main();
//비동기 실행이 밑으로 흘러가도록 할 수 있다.
catch
이번엔 reject를 선언하였을때 처리하는 방법이다.
function p(ms) {
return new Promise((resolve,reject)=>{
setTimeout(() => {
reject(new Error('reason'));
}, ms);
});
}
(async function main() {
try {
const ms = await p(1000);
} catch (error) { //error에 위에서 만든 Error객체가 들어간다.
console.log(error);
}
})();
역시 비동기 방식안에서 진행되고 try, catch를 사용하면 reject가 실행되게 된다.
특히 async function 은 Promise.resolve 함수로 감싸져서 리턴되게 된다.
function p(ms) {
return new Promise((resolve,reject)=>{
setTimeout(() => {
resolve(ms);
// reject(new Error('reason'));
}, ms);
});
}
async function asyncP(){
const ms = await p(1000);
return 'mingyu: ' + ms;
}
(async function main() {
try {
const name = await asyncP();
console.log(name);
} catch (error) {
}
})();
즉, asyncP() 가 호출된 이후 -> 1초를 기다리고 resolve(ms) 실행 -> mingyu : + ms가 Promise.resolve로 감사져서 리턴되어 그 값이 출력되는 것이다.
이 역시 finally를 적용할 수 있으며, resolve와 reject를 선택함에 따라 동작이 달라지게 된다.
function p(ms) {
return new Promise((resolve,reject)=>{
setTimeout(() => {
//resolve(ms); // resolve 실행
reject(new Error('reason')); //reject가 실행
}, ms);
});
}
async function asyncP(){
const ms = await p(1000);
return 'mingyu: ' + ms;
}
(async function main() {
try {
const name = await asyncP();
console.log(name);
} catch (error) {
console.log(error);
} finally{
console.log('end')
}
})();
이해가 잘 안될 수도 있는데 아래 예제를 보면 조금 더 직관적으로 다가올 것이다.
function p(ms) {
return new Promise((resolve,reject)=>{
setTimeout(() => {
resolve(ms); // resolve 실행
//reject(new Error('reason')); //reject가 실행
}, ms);
});
}
p(1000)
.then(() => p(1000))
.then(() => p(1000))
.then(() => {
console.log('3000ms 후 실행')
});
(async function main() {
await p(1000);
await p(1000);
await p(1000);
console.log('3000ms 후 실행')
})();
즉 그냥 Promise만 사용하면 체인형식처럼 이어지는 반면, async를 사용하면 체인보다는 하나가 실행되고 기다리고 실행되는 형식처럼 보일 것이다.
물론 all과 race도 사용할 수 있다.
function p(ms) {
return new Promise((resolve,reject)=>{
setTimeout(() => {
resolve(ms); // resolve 실행
//reject(new Error('reason')); //reject가 실행
}, ms);
});
}
(async function main() {
const results = await Promise.all([p(1000),p(2000),p(3000)]);
console.log(results); //[1000, 2000, 3000]
})();
(async function main() {
const result = await Promise.race([p(1000),p(2000),p(3000)]);
console.log(result); //1000
})();
'FrontEnd > JavaScript' 카테고리의 다른 글
07_유용한 JS지식 (0) | 2021.12.19 |
---|---|
06_JS_배열 내장함수 (0) | 2021.12.18 |
04_JS_클래스 (0) | 2021.12.03 |
03_JS_객체 (0) | 2021.12.03 |
02_JS_조건,반복,함수 (0) | 2021.12.03 |