자바스크립트 에서는 함수의 비동기 처리를 지원합니다.
비동기 처리란 함수를 실행후에 함수의 결과를 기다리지 않고 다음 코드의 실행을 할수 있게 해주는 것을 말합니다.
하지만 구현을 하다보면 새로운 함수에서 이전에 실행된 함수의 실행을 필요로 할때가 있습니다.
let test = () => {
setTimeout(() => {
console.log('a');
setTimeout(() => {
console.log('b');
setTimeout(() => {
console.log('c');
setTimeout(() => {
console.log('d');
setTimeout(() => {
console.log('e')
}, 1000)
}, 1000)
}, 1000)
}, 1000)
}, 1000)
}
test();
// a
// b
// c
// d
// e
위의 코드는 1초마다 console.log를 수행하는 코드입니다.
a를 찍고 나서 1초 뒤에 b를 찍고 1초 뒤에 c를 찍는 형식입니다.
setTimeout은 (함수, 시간) 값을 인자로 받습니다.
만약 알파벳 z까지 찍어야 한다면 setTimeout은 총 26개가 필요하게 되고 코드 또한 매우 복잡해 집니다.
이러한 복잡한 비동기 처리를 위해 es6에서 Promise가 등장하였습니다.
Promise는 클래스이고, state 값을 가집니다.
state는 3개의 상태가 존재합니다.
- pending -> 아직 수행 전
- fulfuilled -> 수행 후
- rejected -> 에러 발생
위의 코드를 Promise를 이용하여 변경해 보겠습니다.
let test = () => {
return new Promise((resolve) => {
setTimeout(() => {
console.log('a')
resolve(new Promise((resolve) => {
setTimeout(() => {
console.log('b')
resolve(new Promise((resolve) => {
setTimeout(() => {
console.log('c');
resolve(new Promise((resolve) => {
setTimeout(() => {
console.log('d')
resolve(new Promise((resolve) => {
setTimeout(() => {
console.log('e');
resolve()
}, 1000)
}))
}, 1000)
}))
}, 1000)
}))
}, 1000)
}));
}, 1000)
})
}
test();
더 복잡해진 것을 확인할 수 있습니다.
위의 코드는 promise를 nested하게 사용한 것인데
promise를 nested하게 사용되면 더 복잡해질수도 있다는 것을 보여드리기 위해 작성한 코드입니다.
이제 nested Promise를 사용하지 않아보겠습니다.
let a = () =>
new Promise((resolve, reject) => setTimeout(() => resolve("a"), 1000));
let b = () =>
new Promise((resolve, reject) => setTimeout(() => resolve("b"), 1000));
let c = () =>
new Promise((resolve, reject) => setTimeout(() => resolve("c"), 1000));
let d = () =>
new Promise((resolve, reject) => setTimeout(() => resolve("d"), 1000));
let e = () =>
new Promise((resolve, reject) => setTimeout(() => resolve("e"), 1000));
test = () => {
a()
.then((data) => {
console.log(data);
return b();
})
.then((data) => {
console.log(data);
return c();
})
.then((data) => {
console.log(data);
return d()
})
.then((data) => {
console.log(data);
return e()
})
}
test()
위의 콜백, nestedPromise에 비해 훨씬 깔끔해진 것을 확인할 수 있습니다.
resolve는 값 혹은 새로운 promise객체를 반환할 수 있습니다.
resolve의 인자값을 다음 then에서 인자로 받게 됩니다.
const a = new Promise((resolve) => resolve('a'))
a.then(data => console.log(data); // a
const b = new Promise((resolve) => resolve('b'))
b.then(data => console.log(data); // b
resolve는 정상적으로 실행되었을때
reject는 error를 반환하기 위해 사용합니다.
resolve를 사용하게 되면 promise의 state값은 fulfilled,
rejected를 사용하게 되면 promise의 state값은 rejected가 됩니다.
앞의 예제를 줄이고 promise내에서 reject를 반환해보겠습니다.
let a = () =>
new Promise((resolve, reject) => setTimeout(() => resolve("a"), 1000));
let b = () =>
new Promise((resolve, reject) => setTimeout(() => resolve("b"), 1000));
let c = () =>
new Promise((resolve, reject) => setTimeout(() => reject(new Error('this is error')), 1000));
test = () => {
a()
.then((data) => {
console.log(data);
return b();
})
.then((data) => {
console.log(data);
return c();
})
.then((data) => {
console.log(data);
})
.catch(err => console.log(err))
}
test()
/*
a
b
Error: this is error
at Timeout._onTimeout ()
at listOnTimeout (node:internal/timers:557:17)
at processTimers (node:internal/timers:500:7) */
promise인스턴스는 catch 메소드를 사용할 수 있습니다.
reject로 뱉어낸 error를 catch 메소드로 잡아낼 수 있습니다.
이렇게 error가 발생했을때 맞는 action을 처리해주어 사용자에게 정보를 제공하여야 합니다.
아래의 그림은 Promise를 도식화한 것입니다.
promise에는 promise 객체들을 병렬적으로 실행하기 위한 도구가 존재합니다.
Promise.all, Promise.allSettled, Promise.any, Promise.race 가 있습니다.
let a = () =>
new Promise((resolve, reject) => setTimeout(() => resolve("a"), 1000));
let b = () =>
new Promise((resolve, reject) => setTimeout(() => resolve("b"), 1000));
let c = () =>
new Promise((resolve, reject) => setTimeout(() => reject(new Error('this is error')), 1000));
Promise.all([a(), b()]).then(data => console.log(data))
// 전부 성공해야 반환, 실패하면 실패한 것 반환
// [ 'a', 'b' ]
Promise.allSettled([a(), b(), c()]).then(data => console.log(data));
// status와 value를 같이 반환
/*
[
{ status: 'fulfilled', value: 'a' },
{ status: 'fulfilled', value: 'b' },
{
status: 'rejected',
reason: Error: this is error
at Timeout._onTimeout (/Users/woo_bottle/Repository/woo_bottle/TIL/test.js:76:60)
at listOnTimeout (node:internal/timers:557:17)
at processTimers (node:internal/timers:500:7)
}
]
*/
Promise.any([a(), b(), c()]).then(data => console.log(data))
// 주어진 모든 프로미스 중 하나라도 이행하는 순간 반환
// a
Promise.race([a(), b(), c()]).then((data) => console.log(data));
// 가장먼저 실행되는 것 반환
// a
- Promise.all -> 모든 promise 객체가 실행되면 결과값을, 하나라도 실패하면 실패한 결과값을 반환합니다.
- Promise.allSettled -> 모든 promise가 처리되면 실패여부와 상관없이 status와 결과값들을 반환합니다.
- Promise.any -> 모든 promise중 하나라도 이행되는 순간 이행된 결과값을 반환합니다
- Promise.race -> 모든 promise중 가장먼저 실행되는 것을 반환합니다.
Promise 보다 더 가독성있게 작성할 수 있는 async/await 이 es8에 등장하였습니다.
위의 a부터 e까지 출력하는 promise코드를 async/await으로 작성해 보도록 하겠습니다.
let a = () =>
new Promise((resolve, reject) => setTimeout(() => resolve("a"), 1000));
let b = () =>
new Promise((resolve, reject) => setTimeout(() => resolve("b"), 1000));
let c = () =>
new Promise((resolve, reject) => setTimeout(() => resolve('c'), 1000));
let d = () =>
new Promise((resolve, reject) => setTimeout(() => resolve("d"), 1000));
let e = () =>
new Promise((resolve, reject) => setTimeout(() => resolve("e"), 1000));
(async function(){
console.log(await a());
console.log(await b());
console.log(await c());
console.log(await d());
console.log(await e());
}())
위의 코드는 같은 결과값을 반환합니다.
코드는 훨씬 짧아진 것을 확인할 수 있습니다.
좀 더 유지보수에 효율적인 코드를 작성할 수 있을것만 같습니다.
async로 실행 함수를 감싸주어야 하고 await을 Promise 객체 앞에 작성해주면 코드의 동기적 실행을 수행할 수 있습니다.
async/await의 예외처리를 해줄때는 try~catch를 이용합니다.
let a = () =>
new Promise((resolve, reject) => setTimeout(() => resolve("a"), 1000));
let b = () =>
new Promise((resolve, reject) => setTimeout(() => resolve("b"), 1000));
let c = () =>
new Promise((resolve, reject) => setTimeout(() => reject(new Error('c')), 1000));
let d = () =>
new Promise((resolve, reject) => setTimeout(() => resolve("d"), 1000));
let e = () =>
new Promise((resolve, reject) => setTimeout(() => resolve("e"), 1000));
(async function () {
try {
console.log(await a());
console.log(await b());
console.log(await c());
console.log(await d());
console.log(await e());
} catch(err) {
console.log(err);
}
})();
/*
a
b
Error: c
at Timeout._onTimeout
at listOnTimeout (node:internal/timers:557:17)
at processTimers (node:internal/timers:500:7)
*/
c에서 error를 반환하게 하였습니다.
이때 catch 구문에서 에러를 출력해주는것을 확인할 수 있습니다!!
이런 async/await을 사용할때 forEach, map과 사용할때는 원하는대로 동작하지 않을수 있습니다.
이때는 promise.all과 같은 병렬 수행 도구를 이용하는 것이 좋습니다.
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise
'Frontend > Javascript' 카테고리의 다른 글
Javascript 화살표 함수 (0) | 2022.03.25 |
---|---|
Javascript event capturing, bubbling (0) | 2022.03.25 |
Javascript mixins (0) | 2022.03.21 |
Javascript mouseup, mousedown, mousemove, touchstart, touchend, touchmove (0) | 2022.03.20 |
Javascript keydown, keyup, keypress (0) | 2022.03.19 |