JavaScript는 비동기와 non-blocking 방식이기 때문에 현재 실행 중인 코드가 끝나지 않아도 다음 코드를 실행한다.
async function main() {
function first() {
// 1
console.log("first");
}
// 1초 뒤에 first 호출
setTimeout(first, 1000);
// 2
console.log("middle");
// 3
console.log("last");
}
main();
이 코드를 실행했을 때 동기적 언어라면 출력 순서가 1초 후 1 -> 2 -> 3이 되지만
자바스크립트는 async, non-blocking이기 때문에 1에서 대기하지 않고 2 -> 3, 그리고 1초 뒤에 1이 출력된다.
JavaScript에서 비동기적 처리를 동기적으로 처리할 수 있게 돕는 객체이다.
async function main() {
// 1
const timerPromise = new Promise((resolve, reject) => {
// 이곳에 정의된 함수가 executor
// 1초 후 setTimeout 내부 코드 실행
setTimeout(() => {
console.log("First");
resolve("Resolve!"); // resolve 호출 시 매개변수 전달 가능
}, 1000);
});
// 이 시점에서 timerPromise는 Fulfilled Promise라고 부를 수 있다.
// 2
// resolve에서 전달한 매개변수를 받을 수 있음
timerPromise.then((data) => {
console.log("Middle");
console.log("Last");
console.log(data);
});
// 출력결과:
// (1초 후)First
// Middle
// Last
// Resolve!
}
main();
async function promiseCatch() {
// 1
const errorPromise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("First");
// 위의 예제와 똑같은데 resolve 대신에 reject 호출
reject("Error!!");
}, 1000);
});
// errorPromise 객체 내부에서 reject 또는 resolve가 실행되었을 때 상태가 바뀌는데, resolve일 때는 then 메소드, reject일 때는 catch 메소드가 실행됨
errorPromise
.then(() => {
console.log("Middle");
console.log("Last");
})
.catch((error) => {
console.log("에러 발생!", error);
});
}
// 출력 결과:
// (1초 후)First
// 에러 발생! Error!!
const promise = Promise.resolve('first');
promise.then((value) => { console.log(data); });
// 출력 결과: first
const firstPromise = Promise.resolve('first');
// console.log의 함수 호출이 아니라 함수 자체를 넘겼기 때문에 resolve로부터 then이 넘겨받은 'first'가 then의 매개변수인 함수의 매개변수로 들어감
firstPromise.then(console.log);
// 출력 결과: first
const countPromise = Promise.resolve(0);
function increment(value) {
return value + 1;
}
const resultPromise = countPromise
.then(increment) // 처음 넘겨받은 인자에 대해서 increment 실행
.then(increment) // 한번 더
.then(increment); // 한번 더
resultPromise.then(console.log); // 최종 값 출력
// 출력 결과: 3
일반적인 함수 혹은 화살표 함수와 아주 비슷하지만 2가지 차이점이 있다.
// 일반 함수
async function func() { return Promise.resolve("data"); }
async function func2() { return "data"; }
// 익명 함수
async function() {}
// 화살표 함수
async () => {}
func();
func2()
출력 결과: Promise { 'data' } (func, func2의 결과값은 똑같다. 자동으로 Promise 객체로 resolve 된다)
위의 Promise.resolve 메소드 실습을 하면서 비동기적 처리 방법을 익혔는데, 3번째 예시같은 경우 then을 여러번 중첩하는 등 콜백 함수를 지나치게 많이 쓰면 콜백 지옥이라고 할 정도로 가독성과 효율이 좋지 않게 된다.
이를 방지하는 것이 비동기 함수의 await 연산자이다. await 연산자를 사용하면 콜백 함수를 여러 번 사용하지 않고 비동기적 처리를 할 수 있기 때문에 코드가 간결해진다.
또한 new Promise(executor)로 Promise 객체를 직접 생성하면 executor가 바로 실행되는데, 비동기 함수를 사용하면 함수를 호출하기 전까지 Promise가 생성되지 않는다.
const data = await "value";
// 1
function setTimeoutFunc(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(time + "ms가 지났습니다.");
resolve();
}, time);
});
}
async function main() {
console.log("시작되었습니다.");
// 2
await setTimeoutFunc(1000);
console.log("종료되었습니다.");
}
main();
// 출력 결과:
// 시작되었습니다.
// 1000ms가 지났습니다.
// 종료되었습니다.