export 키워드로 내보내진 변수, 함수 등등을 불러올 수 있는 키워드
import defaultExport from "module-name";
module-name 내에 export default로 내보내진 것을 가져온다. 보통 컴포넌트들을 이렇게 사용한다.
import * as allItems from "module-name";
module-name 내에서 export 된 모든 것을 모두 가져온다.
as 이후 이름은 중복되지만 않으면 자유롭게 정할 수 있다.
import {loadItem} from "module-name";
module-name 내에서 export 된 것 중에 특정 값만 가져온다.
사용할 라이브러리만 가져오는 것이 효율적이다.
import {loadItem as loadSometing} from "module-name"
module-name 내에서 export 된 것 중에 특정 값만 이름을 바꿔서 가져온다.
loadItem을 loadSometing으로 가져온다.
import App, {printToday} from "module-name"
export default로 된 모듈을 가져오는 것과 export로 된 모듈을 가져오는 것을 같이 import 할 수 있다.
특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행하는 자바스크립트의 특성이다.
동기: 10 + 10 + 10 = 30
비동기: 10 + 10 + 10 = 10
function onButtonClick() {
alert("눌렀군요");
}
document.querySelector(".save-button").addEventListener("click", onButtonClick);
function work() {
console.log("work!");
}
setTimeout(work, 1000); // 1초 후 실행
setInterval(work, 5000); // 5초마다 반복
console.log("work process");
// 출력 결과
work process
work!
work!
function work() {
console.log("작업 시작");
setTimeout(function () {
console.log("작업 중...");
}, 0);
console.log("작업 종료");
}
// 출력 결과
작업 시작
작업 종료
작업 중...
function request(url, successCallback, failCallback) {
const xhr = new XMLHttpRequest();
xhr.addEventListener("load", (e) => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
successCallback(JSON.parse(xhr.responseText));
} else {
failCallback(xhr.statusText);
}
}
});
xhr.addEventListener("error", (e) => failCallback(xhr.statusText));
xhr.open("GET", url);
xhr.send();
}
콜백 지옥에 빠지게 된다.
물론 동기적으로 코드를 작성할 수도 있다.
하지만 자바스크립트는 싱글 스레드이기 때문에 동기적으로 작성하면 요청 후 응답이 오기 전까지 브라우저가 굳어버린다.
예를 들어서 API를 조회하는 데 10초가 걸린다면 10초 동안 브라우저는 먹통이 될 것이다.
따라서 비동기적으로 작성하는 코드가 좋은 코드라고 볼 수 있다.
Promise는 비동기 작업을 제어하기 위해 나온 개념으로, 콜백 지옥에서 어느 정도 벗어날 수 있게 해준다.
promise로 정의된 작업끼리는 연결할 수 있으며, 이를 통해 코드의 depth가 크게 증가하지 않는 효과가 있다.
const promise = new Promise((resolve, reject) => {
// promise 내부에서 비동기 상황이 종료될 때, resolve 함수 호출
// promise 내부에서 오류 상황일 때, reject 함수 호출
});
Promise에서는 then을 이용해 비동기 작업 이후 실행할 작업을 지정합니다.
function asyncPromiseWork() {
// code...
return new Promise((resolve, reject) => {
// code...
return resolve("complete");
});
}
asyncPromiseWork().then((result) => console.log(result));
Promise의 then 내에서 Promise를 return 할 경우 이어진다.
// Callback
callbackWork((result) => {
callbackWork((result) => {
callbackWork((result) => {
callbackWork(result);
});
});
});
// Promise
promiseWork()
.then((result) => {
return promiseNextWork(result);
})
.then((result) => {
return promiseThirdWork(result);
})
.then((result) => {
return promiseFinalWork(result);
});
Promise chain 중 작업이 실패했을 경우 .catch로 잡을 수 있다.
catch를 작성하지 않을 경우 promise chain 중 에러가 발생했을 때 chain이 멈추니 가급적 작성하는 것이 좋다.
promiseWork()
.then((result) => {
return promiseNextWork(result);
})
.then((result) => {
return promiseThirdWork(result);
})
.then((result) => {
return promiseFinalWork(result);
})
.catch((e) => {
alert("에러가 발생했습니다.");
});
성공과 실패 여부와 상관없이 호출해야 하는 코드가 있다면 finally에서 처리한다.
promiseWork()
.then((result) => {
return promiseNextWork(result);
})
.then((result) => {
return promiseThirdWork(result);
})
.then((result) => {
return promiseFinalWork(result);
})
.catch((e) => {
alert("에러가 발생했습니다.");
})
.finally(() => {
alert("작업이 끝나면 무조건 실행합니다.");
});
여러 promise를 동시에 처리할 때 유용하다.
const promise1 = delay(1000)
const promise1 = delay(2000)
const promise1 = delay(3000)
Promise.all([promise1, promise2, promise3]).then(() => {
// promise1, promise2, promise3이 모두 처리된 이후 호출
})
여러 promise 중 하나라도 resolve 혹은 reject가 되면 종료된다.
거의 사용하지는 않는다.
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
const promise = [1, 2, 3, 4, 5].map((n) => {
const delayTime = getRandomInt(1000, 5000);
return new Promise((resolve) => {
setTimeout(() => {
console.log(`${n}번 고양이 완주!`);
resolve(`${n}번 고양이 승리!`);
}, delyaTime);
});
});
Promise.race(promises).then(message => console.log(message))
출력 결과
5번 고양이 완주!
5번 고양이 승리! // resolve
2번 고양이 완주!
4번 고양이 완주!
1번 고양이 완주!
여러 promise 중 하나라도 resolve 되면 종료된다.
race와 다르게 reject는 무시한다.
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
const promise = [1, 2, 3, 4, 5].map((n) => {
const delayTime = getRandomInt(1000, 5000);
return new Promise((resolve, reject) => {
if (n === 1){
return reject(`${n}번 고양이 기권!`)
}
setTimeout(() => {
console.log(`${n}번 고양이 완주!`);
resolve(`${n}번 고양이 승리!`);
}, delyaTime);
});
});
Promise.any(promises).then(message => console.log(message))
출력 결과
// 1번 고양이는 무시된다.
4번 고양이 완주!
4번 고양이 승리!
3번 고양이 완주!
5번 고양이 완주!
2번 고양이 완주!
여러 Promise들이 성공했거나 실패했거나 상관없이 모두 이행된 경우를 처리할 수 있다.
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
const promise = [1, 2, 3, 4, 5].map((n) => {
const delayTime = getRandomInt(1000, 5000);
return new Promise((resolve, reject) => {
if (n % 2 === 0) {
return reject(`${n}번 고양이 완주 실패!`);
}
setTimeout(() => {
resolve(`${n}번 고양이 완주!`);
}, delyaTime);
});
});
Promise.allSettled(promise).then((results) => console.log(results));
출력 결과
// 5번 다 then을 실행
(5) [{...}, {...}, {...}, {...}, {...}]
0: {status: "fulfilled", value: "1번 고양이 완주!"}
1: {status: "rejected", value: "2번 고양이 완주 실패!"}
2: {status: "fulfilled", value: "3번 고양이 완주!"}
3: {status: "rejected", value: "4번 고양이 완주 실패!"}
4: {status: "fulfilled", value: "5번 고양이 완주!"}
주어진 값으로 이행하는 Promise.then 객체를 만든다.
반환하는 값을 promise 타입으로 만들 때 유용하다.
주어진 값이 Promise인 경우 해당 Promise가 반환된다.
const cached = {
roto: "bassist",
};
const findMember = (memberName) => {
if (cached[memberName]) {
return Promise.resolve(cached[memberName]);
} // cache가 있으면 promise로 리턴
return request(`/members/${memberName}`).then((member) => {
cache[member.memberName] = memberName;
return meberName; // cache가 없으면 api 호출
});
};
findMerber("roto").then((memberName) => console.log(memberName));
주어진 값으로 reject 처리 된 Promise.then 객체를 만든다.
주어진 값이 Promise인 경우 해당 Promise가 반환된다.
강제로 reject를 시켜야 되는 상황에 사용할 수 있다.
거의 사용하지 않는다.
new Promise((resolve, reject) => reject());
Promise가 callback depth를 1단계로 줄여주긴 하지만, 여전히 불편하다.
코드의 실행이 위에서 아래로 실행되는 것이 아니기 때문에 코드의 가독성을 어렵게 만드는 부분이 있다.
// Promise
const delay = (delayTime) => {
rerturn new Promise(resolve => setTimeout(resolve, delayTime))
}
const work = () => {
console.log("work run");
delay(1000)
.then(() => {
console.log("work 1 complete.");
return delay(1000);
})
.then(() => {
console.log("work 2 complete.");
return delay(1000);
})
.then(() => {
console.log("work 3 complete.");
return delay(1000);
})
.then(() => {
console.log("work all complete!");
});
console.log("work running...");
};
work();
출력 결과
work run
work running
work 1 complete.
work 2 complete.
work 3 complete.
work all complete!
하지만 async, await를 사용하면 Promise를 비동기로 실행하면서 동기 코드처럼 보이게 작성할 수 있다.
const work = async () => {
console.log("work run");
await delay(1000);
console.log("work 1 complete.");
await delay(1000);
console.log("work 2 complete.");
await delay(1000);
console.log("work 3 complete.");
await delay(1000);
console.log("work all complete!");
};
work();
async await 사용 방법
async가 함수 앞에 붙어야지 함수 내부에서 await를 사용할 수 있다.
async 키워드가 붙은 함수는 실행 결과를 Promise로 감싼다. (반환 값이 자동으로 Promise 타입으로 변환됨)
에러 처리는 try catch 문을 사용하면 된다.
// function 키워드를 사용하는 경우
async function asyncFunction() {
const res = await request(...)
}
// arrow function을 사용하는 경우
const asyncFunction = async () => {
const res = await request(...)
}
API 호출 예시
try {
const data = await request(`https://kdt-frontend.programmers.co.kr/comments?todo.id=${id}`);
this.setState({
...this.state,
comments: data,
});
} catch (error) {
// promise의 .catch와 비슷한 역할
} finally {
// promise의 .finally와 비슷한 역할
}