JS는 싱글 스레드 프로그래밍 언어이기 때문에 멀티 작업을 하기 위해서는 비동기 처리 방식이 자주 쓰인다고 한다.
동기 방식 : 어떠한 일을 처리하는 동안 다음 작업이 수행하지 못하고 첫번째 작업이 끝난 후 다음 동작이 가능한 것.
비동기 방식 : 첫번째 작업이 수행되고 응답을 기다리는 동안 뒤의 동작을 먼저 실행하여 동시에 작업을 처리할 수 있는 방식.
장단점
- 동기
- 장점 : 설계가 간단하고, 직관적
- 단점 : 요청에 대한 결과가 반환되기 전까지 대기해야 해서 런타임이 길어짐.
콜백 함수를 사용할 때 콜백 함수 Hell이 있었고 이를 개선한 Promise 객체도 서비스 규모가 커지고 코드가 복잡해지면 중첩 사용으로 가독성 떨어지고 유지보수가 어렵다고 한다.
async, await는 Promise를 대체하기 위한 기능이 아니다. 위의 단점들을 해결하기위해 문법만 다르게 해줄 뿐이다.
사용하는데 어려움은 없다.
function 앞에 async, 비동기로 처리되는 부분 앞에는 await를 붙여주면 된다.
// 프로미스 객체 반환 함수
function delay(s) {
return new Promise(resolve => {
setTimeout(() => {
console.log(`${s * 0.001} 초가 지났습니다.`);
resolve()
}, s);
});
}
// async/await 방식
async function main() {
await delay(1000);
await delay(2000);
const result = await Promise.resolve('끝');
console.log(result);
}
// 메인 함수 호출
main();
Promise는 Caller(Executor)와 Callee(Callback)가 하나의 Promise 객체로 뭉쳐진 것이다.
**async** function caller() {}const result = **await** caller()위에서 Promise 객체를 반환하면 항상 Promise{} 라는 상자 안에 상태가 출력되었었는데 await로 상자를 열어줄 수 있다.
**async** = Promise 상자반환 (Promise.resolve 로 감싸져있으면 바로 반환, 아니면 상자 포장 반환)**await** = Promise 상자열기 (Promise 객체를 기다렸다가 상자를 열어 내부 값을 반환.async가 붙은 함수는 반드시 Promise를 반환하고 반환값이 Promise가 아닌 것은 Promise로 감싸 반환한다.
await 를 만나면 Promise가 처리될 때까지 기다린다.
async function returningpromise() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve('완료!'), 1000)
});
let result = await promise // 프라미스를 기다리지않고, 프라미스 객체 그대로 반환
console.log(result) // Promise { <pending> }
}
returningpromise() // 1초 후 '완료' 출력
await는 Promise 처리가 끝날 때까지 기다린다.
즉 비동기 처리가 완료될 때까지 코드 실행을 일시 중지한다.
Promise 안에서는 무조건 반환을 위해서 Resolve 사용한다. return 사용하는 것은 아니다.
function hello() {
return new Promise((resolve, reject) => {
resolve('aaron')
})
}
const user = hello()
user.then(console.log) // 'aaron'
console.log(user) // Promise { 'aaron' }
return 으로 하면 Promise{<pending>} 상태가 된다.
하지만 Async 함수에서는 그냥 return 사용해도 된다. Promise 로 감싸 반환해도 됨
async function hello() {
return 'magnus'
}
const user = hello()
user.then(console.log) // 'magnus'
async function hello() {
return new Promise((resolve, reject) => {
resolve('magnus')
})
}
const user = hello()
user.then(console.log) // 'magnus'
Promise.then() 방식할 때 에러 처리를 계속 .catch() 로 진행했었다.
이 에러 처리를 try-catch를 사용하여 해결할 수 있다.
async function goThrows() {
console.log('try catch 시작');
throw new Error('throw new Error form goThrows()');
}
// await를 사용하기 위해 async 키워드 사용
async function run() {
try {
// 비동기 함수 앞에 await 키워드 사용
await goThrows();
console.log('시도');
} catch(err) {
console.error('try 에서 예외 발생 시');
} finally {
console.log('마지막 실행');
}
}
run();
/* 실행 결과
'try catch 시작'
'try 에서 예외 발생 시'
'마지막 실행' */
goThrows() 함수에서 throw new Error('throw new Error form goThrows()'); 예외를 발생시켰다.
따라서 catch() 로 이동하여 출력한다.
프로미스 객체 함수를 await와 같이 사용해서 실행시키는 것이 아닌 미리 함수를 동기/논블록킹으로 실행하고 그 결과 프로미스값을 await를 통해 받을 수 있다.
총 2초의 시간이 걸림.
async function getFruites() {
let a = await getApple(); // getApple() 비동기 처리를 요청하고, 요청이 처리될때 까지 기다림 (1초 소요)
let b = await getBanana(); // getBanana() 비동기 처리를 요청하고, 요청이 처리될때 까지 기다림 (1초 소요)
console.log(`${a} and ${b}`);
}
반드시 순서를 지켜야하는 경우가 아니라면 비동기 처리 요청과 값을 await 하는 로직을 분리시켜서 작성해도 된다.
async function getFruites(){
let getApplePromise = getApple(); // async함수를 미리 논블록킹으로 실행한다.
let getBananaPromise = getBanana(); // async함수를 미리 논블록킹으로 실행한다.
// 이렇게 하면 각각 백단에서 독립적으로 거의 동시에 실행되게 된다.
console.log(getApplePromise)
console.log(getBananaPromise)
let a = await getApplePromise; // 위에서 받은 프로미스객체 결과 변수를 await을 통해 꺼낸다.
let b = await getBananaPromise; // 위에서 받은 프로미스객체 결과 변수를 await을 통해 꺼낸다.
console.log(`${a} and ${b}`); // 본래라면 1초+1초 를 기다려야 하는데, 위에서 1초기다리는 함수를 바로 연속으로 비동기로 불려왔기 때문에, 대충 1.01초만 기다리면 처리된다.
})
Promise.all()정적 메서드를 사용하는 방법도 있다.
비동기 처리 완료 시점을 알기 어려울 때에 실무에서는 Promise.all()로 처리한다고 한다.
비동기 함수들이 모두 resolve가 되어야 결과를 반환받는다.
function getApple(){
return new Promise( (resolve, reject) => {
setTimeout(() => resolve("apple"), 1000);
})
}
function getBanana(){
return new Promise( (resolve, reject) => {
setTimeout(() => resolve("banana"), 1000);
})
}
async function getFruites(){
console.time();
// 구조 분해로 각 프로미스 리턴값들을 변수에 담는다.
let [ a, b ] = await Promise.all([getApple(), getBanana()]);
console.log(`${a} and ${b}`);
console.timeEnd();
}
getFruites();
Reference
🔗 https://developer-talk.tistory.com/370 - async, await 예외 처리
🔗 async, await 개념
🔗 https://www.youtube.com/watch?v=aoQSOZfz3vQ - 비동기의 꽃 드림코딩 영상