프로젝트를 진행하면서 API 통신을 위해 비동기 처리를 많이 하게 됐다.
그러면서 "항상 async/await으로 처리해야 하는 건가?"라는 의문이 생겼고, "async/await"이 없을 때는 어떻게 처리했을까? 궁금해서 비동기 처리에 대한 글을 쓰게 됐다.
자바스크립트는 기본적으로 동기적으로 동작한다. 위에서부터 한 줄씩 차례대로 실행되는 방식이다.
하지만 경우에 따라서는 비동기 처리도 가능하다. 쉽게 말해, 자바스크립트는 "언제 끝날지 모르는 코드"를 굳이 기다리지 않고, 나머지 코드를 먼저 실행한다.
이처럼 특정 작업이 끝나기를 기다리지 않고 진행하는 방식을 비동기 처리라고 한다.
자바스크립트에서 비동기를 다루는 방법은 대표적으로 세 가지가 있다.
비동기 처리에서도 "콜백함수"를 이용하면 코드의 실행 순서를 보장받을 수 있다.
function getData(callbackFunc) {
$.get('https://domain.com/products/1', function(response) {
callbackFunc(response); // 서버에서 받은 데이터 response를 callbackFunc()에 넘겨줌
});
}
getData(function(tableData) {
console.log(tableData); // $.get()의 response 값이 tableData에 전달됨
});
단점은 "콜백 지옥"이다. 비동기 처리가 이어진다면, 콜백 안에 콜백을 써야 하는 형식이 된다.
$.get('url', function(response) {
parseValue(response, function(id) {
auth(id, function(result) {
display(result, function(text) {
console.log(text);
});
});
});
});
중첩 선언한 부분을 각 콜백 함수를 분리한다면 가독성을 높일 수 있다. 하지만 여전히 복잡하다.
function parseValueDone(id) {
auth(id, authDone);
}
function authDone(result) {
display(result, displayDone);
}
function displayDone(text) {
console.log(text);
}
$.get('url', function(response) {
parseValue(response, parseValueDone);
});
프로미스는 자바스크립트 비동기 처리를 위한 객체이다.
function getData(callback) {
// new Promise() 추가
return new Promise(function(resolve, reject) {
$.get('url 주소/products/1', function(response) {
resolve(response); // 데이터를 받으면 resolve() 호출
});
});
}
// getData()의 실행이 끝나면 호출되는 then()
getData().then(function(tableData) {
// resolve()의 결과 값이 여기로 전달됨
console.log(tableData); // $.get()의 reponse 값이 tableData에 전달됨
});
프로미스 객체를 만들면(new Promise) 프로미스는 3가지 상태를 갖는다.
여기서 말하는 상태란 프로미스의 처리과정을 의미한다.
프로미스의 콜백함수 인자로 resolve랑 reject를 받는다.

"콜백지옥"이 일어나지 않는 이유는 "프로미스 체이닝" 덕분이다.
.then().then().then() ... catch() 형식으로 체이닝을 이어갈 수 있다.
단, then과 catch는 promise 객체여야만 유효하게 동작하다. then이나 catch는 항상 Promise를 반환한다.
Q. 그럼 catct가 반환하는 상태는 무엇일까?
원래 Promise(reject)는 rejected이다. 하지만 catch 안에서 에러를 정상적으로 처리했기 때문에, catch가 반환하는 새 Promise는 fulfilled(undefined) 상태가 된다.
catch 안에서 다시 에러가 발생하면, 그 시점의 Promise는 다시 rejected 상태로 바뀌어 다음 catch로 흘러간다.
function getData() {
return new Promise((resolve, reject) => {
// ...
});
}
// then() 으로 여러 개의 프로미스를 연결한 형식
getData()
.then(function(data) {
// ...
})
.then(function() {
// ...
})
.then(function() {
// ...
});
프로미스 예외처리는 2가지 방법이 있다.
getData().then(
handleSuccess,
handleError
);
getData().then().catch();
위 2가지 방법 모두 프로미스의 reject() 메서드가 호출되어 실패 상태가 된 경우에 실행된다.
단, 첫 번째 방법은 then 내부에서 발생한 오류를 잡지 못한다.
async/await은 콜백함수와 프로미스의 단점을 보완한다.
더 선언적으로 사용할 수 있다. 데이터를 받아온 시점에 콜백함수나 .then을 사용하지 않아도 된다.
비동기 처리 코드 앞에 await을 붙인다.
일반적으로 HTTP 통신을 하는 프로미스를 반환하는 API 호출 함수 앞에 붙인다.
async function logName() {
var user = await fetchUser('domain.com/users/1');
if (user.id === 1) {
console.log(user.name);
}
}
async/await에서 예외를 처리하는 방법은 try/catch이다. 프로미스에서 에러 처리를 위해 .catch()를 사용했던 것처럼 async에서는 catch{}를 사용한다.