티스토리 뷰
Promise에 대한 기초에 대해 정리해 보려고 한다. 우선은 아래의 코드를 보자. 이해가 되는가? 이해가 되지 않는다면, 이곳에서 알아가 보자.
const Loading = async () => {
await new Promise((resolve) => setTimeout(resolve, 1000))
};
1. promise
promise는 시간이 걸리는 비동기 작업에 대해, 성공/실패를 나타내는 객체이다. 그리고 함수에 콜백을 전달하는것이 아닌 콜백을 첨부하는 객체라고도 할 수 있다. 이말을 조금 더 쉽게 적어보면, 성공/실패를 나타내는 함수를 호출할 수 있게 담아서 전달해 주는 것이라고 할 수 있다. 아래의 예시를 보자.
function successCallback(result) {
console.log("Audio file ready at URL: " + result);
}
function failureCallback(error) {
console.log("Error generating audio file: " + error);
}
createAudioFileAsync(audioSettings, successCallback, failureCallback);
- createAudioFileAsync 함수의 audioSettings에 따라서 성공 시 successCallback 함수를 주고 실패시 failureCallback 함수를 주는 느낌이라고 할 수 있다.
위를 Promise를 반환하도록 수정해 보면 아래와 같다.
createAudioFileAsync(audioSettings).then(successCallback, failureCallback);
그리고 조금 더 간단히 적으면 아래와 같다.
const promise = createAudioFileAsync(audioSettings);
promise.then(successCallback, failureCallback);
- 특징1. promise는 createAudioFileAsync를 완료하기 전에는 then을 실행하지 않는다.
- 특징2. then을 여러번 사용하여 여러개의 콜백을 추가 할 수 있고, 각각의 콜백은 순서대로 실행된다.
2. 연속된 비동기 처리하기
우선 기본적은 연속된 콜백함수를 보면 아래와 같다. 혹자는 콜백지옥이라고도 한다.
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
이를 promise로 만들면 아래와 같다.
doSomething().then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);
- catch는 than(null, failureCallback)의 축약이다.
위를 화살표 함수로 아래와 같이 좀 더 간단히 표현할 수 있다.
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);
- catch는 than(null, failureCallback)의 축약이다.
- return 으로 반환하는 값이 반드시 있어야 한다. 반환값이 없다면 이전 promise의 결과를 받지 못한다.
- 위의 .then(res => do(res)) 는 .then(res => {return do(res)}) 와 같다.
- 기본적으로 연속되는 promise에서는 예외/오류가 발생하면 코드 작동을 멈추고 아래에서 catch를 바로 찾는다. 이를 코드로 작성해보면, 아래와 같은 느낌이라고 이해하면 좋다. 바로 아래 코드는 참고만 하자.
try {
const result = syncDoSomething();
const newResult = syncDoSomethingElse(result);
const finalResult = syncDoThirdThing(newResult);
console.log(`Got the final result: ${finalResult}`);
} catch(error) {
failureCallback(error);
}
추가로 비동기 코드를 async/await 구문을 사용하면 좀 더 매력적인 코드가 아래처럼 된다.
async function foo() {
try {
const result = await doSomething();
const newResult = await doSomethingElse(result);
const finalResult = await doThirdThing(newResult);
console.log(`Got the final result: ${finalResult}`);
} catch(error) {
failureCallback(error);
}
}
3. new Promise
보통 비동기 함수는 promise를 반환하지만, 그렇지 않은 함수도 있다. 가장 대표적인 것이 바로 setTimeout 함수이다. 아래의 예를 보면 saySomething()이 실패/오류 가 있더라도 아무것도 잡아내지 않는다.
setTimeout(() => saySomething("10 seconds passed"), 10000);
이러한 setTimeout에 관한 문제를 해결하는 방법으로는 아래와 같이 Promise로 감싸는 방법이 있다.
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
wait(10000).then(() => saySomething("10 seconds")).catch(failureCallback);
- 위의 코드는 resolve를 사용했다. 기본적으로 setTimeout 함수는 fail/error가 일어나지 않으므로 reject는 사용하지 않고 resolve만 사용했다.
resolve에 대해 조금더 알아보자.
resolve
Promise.resolve(value) 메서드는 Promise.then 객체를 반환한다. 쉽게 말하면, 코드가 resolve를 만나면, then으로 이동해서 코드가 진행된다고 할 수 있다. 아래의 예를 통해 이해를 진행해 보자
const promise1 = Promise.resolve(123);
promise1.then((value) => {
console.log(value);
// expected output: 123
});
- Promise.resolve()에 들어간 인자가 then의 value로 들어가는 것을 확인할 수있다.
var original = Promise.resolve(33);
var cast = Promise.resolve(original);
cast.then(function(value) {
console.log('value: ' + value);
});
console.log('original === cast ? ' + (original === cast));
// 로그 순서:
// original === cast ? true
// value: 33
- console.log를 자세히 보면, 로그의 순서가 반대인 것을 확인 할 수 있다. 이는 then이 비동기로 호출되기 때문이다.
then/catch
then은 resolve를 만나면, 실행된다는 것을 알았다. catch는 reject를 만나면 실행된다.
var p1 = new Promise(function(resolve, reject) {
resolve("성공!");
// 또는
// reject("오류!");
});
p1.then(function(value) {
console.log(value); // 성공!
}, function(reason) {
console.log(reason); // 오류!
});
- 위의 코드는 catch가 없다!! 그럼 reject를 만나면 어떻게 될까?
- 기본적으로 then(null, function()) 과 catch(function())은 같다. 즉, catch를 then의 두번째 인자로 포함시켜 사용해도 된다는 말이다.
- 아래의 예제를 보면 이해가 더 쉽게 된다.
Promise.reject()
.then(() => 99, () => 42) // onRejected에서는 42를 곧 이행할 프로미스로 감싸서 반환함
.then(solution => console.log(solution + '로 이행함')); // 42로 이행함
4. 비동기작업 병렬 처리
Promise.all()
기본적으로 비동기 작업을 병렬로 처리하기 위해서 Promise.all()을 사용한다. 예제를 통해 확인해 보자.
var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("foo");
}, 100);
});
Promise.all([p1, p2, p3]).then(values => {
console.log(values); // [3, 1337, "foo"]
});
- 기본적으로 p1, p2, p3가 모두 완료 될때까지 기다린 후에 then을 시행한다.
- 기본적으로 frontend에서는 화면이 보여지기 전에 데이터를 다 받아오는 것을 기다리면서 사용할 수 있다.
- 만약 병렬로 처리할 작업중에 실패한 작업이 하나라도 있으면 어떻게 될까? 아래의 예를 보자.
var p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('하나'), 1000);
});
var p2 = new Promise((resolve, reject) => {
setTimeout(() => resolve('둘'), 2000);
});
var p3 = new Promise((resolve, reject) => {
setTimeout(() => resolve('셋'), 3000);
});
var p4 = new Promise((resolve, reject) => {
setTimeout(() => resolve('넷'), 4000);
});
var p5 = new Promise((resolve, reject) => {
reject(new Error('거부'));
});
// .catch 사용:
Promise.all([p1, p2, p3, p4, p5])
.then(values => {
console.log(values);
})
.catch(error => {
console.log(error.message)
});
// 콘솔 출력값:
// "거부"
- Promise.all()에서는 하나라도 실패한 작업이 있다면, 실패한 작업만 출력되고 나머지는 사라진다.
let result;
for (const f of [func1, func2, func3]) {
result = await f(result);
}
/* use last result (i.e. result3) */
- 위과 같이 async/await를 사용하여 순차적 구성을 사용할 수 있다.
5. 정리
그렇다면 처음 부분의 코드를 다시 확인해 보자.
const Loading = async () => {
await new Promise((resolve) => setTimeout(resolve, 1000))
};
- setTimeout 함수는 기본적으로 비동기 이다. 이러한 함수는 리턴값이 없기 때문에 Promise로 만들어 리턴값을 받을 수 있게 하여, Loading 이 진행이 완료되는 시점을 파악할 수 있게 만든 로직임을 짐작할 수 있다.
'Web > JAVASCRIPT' 카테고리의 다른 글
디바운싱(debouncing) 기초 정리 (1) | 2022.12.29 |
---|---|
[javascript] 쉽게 이해하는 화살표 함수와 람다 함수 (익명함수) (2) | 2022.04.30 |
[nodejs] dotenv로 중요키 분리하기 (0) | 2021.12.13 |
[nodejs/expess] backend에 session 붙여 login하기 (0) | 2021.12.06 |
[nodejs/express] 핵심 기초 정리 (1) | 2021.11.30 |
- Total
- Today
- Yesterday
- Express
- typescript
- mongoDB
- Deque
- logout
- login
- Queue
- next.config.js
- react autoFocus
- Vue
- useHistory 안됨
- react
- 자연어처리
- django
- nextjs autoFocus
- TensorFlow
- read_csv
- nodejs
- 클라우데라
- UserCreationForm
- JavaScript
- BFS
- useState
- DFS
- error:0308010C:digital envelope routines::unsupported
- NextJS
- pandas
- 자료구조
- Python
- vuejs
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |