벨로퍼트와 함께하는 모던 자바스크립트
React로 넘어가기 전에 빠르게 JavaScript를 복습해본다.
동기적 처리와 비동기적 처리에 대해 알아보자.
특정 함수가 끝난 다음에 어떤 작업을 처리하고 싶다면 callback
함수를 파라미터로 전달해주면 된다.
✔️ callback
함수란 함수 타입의 값을 파라미터로 넘겨줘서, 파라미터로 받은 함수를 특정 작업이 끝나고 호출을 해주는 것이다.
function work(callback){
specificFunction(() => {
...
callback();
});
}
console.log('작업 시작!'); // 1
work(() => { // 3
console.log('작업이 끝났어요!') // 4
});
console.log('다음 작업'); // 2
비동기 작업을 다룰 때에는 callback
함수 외에도 Promise
, 그리고 async
/await
라는 문법을 사용하여 처리 할 수 있습니다.
function iap(n, callback){
setTimeout(() => {
const increased = n + 1;
console.log(increased);
if(callback){
callback(increased);
}
}, 1000);
}
iap(0, n => {
iap (n, n => {
iap (n, n => {
console.log("끝");
});
});
});
결과는 순차대로 나온다. 비동기적으로 처리해야 하는 일이 많아질수록 코드의 깊이는 계속 깊어진다. Promise를 통해 코드의 깊이가 깊어지는 현상을 방지할 수 있다.
const myPromise = new Promise((resolve, reject) => {
// 구현...
})
성공하면 resolve
를, 실패하면 reject
를 호출하면 된다.
가장 기본적인 틀은 다음과 같다.
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
},1000);
});
myPromise
.then(res => {console.log(res);})
.catch(err => {console.log(err);});
응용하면 다음과 같다.
function increaseNprint(n){
return new Promise((resolve, reject) => {
setTimeout(() => {
const value = n + 1;
if(value === 3){
const error = new Error();
error.name = "Value is 3";
reject(error);
return;
}
console.log(value);
resolve(value);
}, 1000);
});
}
increaseNprint(0)
.then(n => {
return increaseNprint(n);
})
.then(n => {
return increaseNprint(n);
})
.catch(e => {
console.error(e);
});
아래에서 increaseNprint에 항상 (n)
을 넣어주지 않아도 .then(increaseNprint)
을 통해 코드를 쓰면 then
에서 알아서 n
을 전달해주므로 코드를 짧게 할 수 있다.
increaseAndPrint(0)
.then(increaseAndPrint)
.then(increaseAndPrint)
.then(increaseAndPrint)
.then(increaseAndPrint)
.then(increaseAndPrint)
.catch(e => {
console.error(e);
});
Promise를 사용하면 비동기 작업의 개수가 많아져도 코드의 깊이가 깊어지지 않게 된다.
async/await는 ES8에 해당하는 문법으로 Promise를 더욱 쉽게 사용할 수 있도록 함.
function sleep(ms){
return new Promise(resolve => setTimeout(resolve, ms));
}
async function process(){
console.log('안녕');
await sleep(1000);
console.log("반갑");
}
process().then(()=> console.log("끝"));
async/await 문법을 사용할 때에는, 함수를 선언할 때 함수 앞부분에 async
키워드를 붙이고 Promise 앞부분에 await
를 넣어주면 해당 Promise가 끝날 때까지 기다렸다가 다음 작업을 수행한다.
함수에서 async
를 사용한다면 해당 함수는 결괏값으로 Promise를 반환한다.
function sleep(ms){
return new Promise(res => setTimeout(res,ms));
}
const getDog = async () => {
await sleep(1000);
return "멍멍이";
}
async function process(){
const dog = await getDog();
console.log(dog);
const dog2 = await getDog();
console.log(dog);
}
process();
async function process(){
const result = await Promise.all([getDog(), getDog()]);
console.log(result);
}
process(); //(2) ["멍멍이", "멍멍이"]
async function process(){
const [dog1, dog2, dog3] = await Promise.all([getDog(), getDog(), getDog()]);
console.log(dog1);
}
process();
✔️ Promise.all
을 사용할 때에는 등록한 Promise 중에 하나라도 실패하면 모든게 실패한 것으로 간주한다.
✔️ Promise.race
는 여러개의 Promise를 등록해서 실행했을 때 가장 빨리 끝난 것 하나만의 결과를 가져온다.
Promise.race
로 인해 결과를 얻었다면 뒤늦게 나온 결과에서 error가 발생하여도 무시된다.