동기 vs 비동기
• 동기적 처리
발생하는 하나의 동작이 모두 끝날 때까지 다른 동작 요청을 처리하지 못한다. 응답이 완료 된 후 다음 동작을 실행하는 것
→ 실행 순서가 확실한 것
- 장점 : 설계가 간단하며 직관적
- 단점 : 요청에 대한 결과가 반환되기 전까지 대기
• 비동기적 처리
다른 일이 끝나는 것과는 상관 없이 다음 일을 수행하는 방식을 의미한다.
→ 실행 순서가 확실하지 않은 것
- 장점 : 요청에 대한 결과가 반환되기 전에 다른 작업을 수행할 수 있어, 자원을 효율적으로 사용 가능
- 단점 : 동기 방식보다 설계가 복잡하고, 논증적
그렇다면 자바스크립트는?
자바스크립트는 싱글스레드(Single Thread) 기반 언어이기 때문에 비동기 처리를 필수적으로 해줘야 한다.
비동기 처리는 결과를 예측할 수 없기 때문에 결과를 예측할 수 있도록 동기식의 처리가 필요하다.
비동기 흐름을 처리하는 방법
1. 콜백 함수 (CallBack hell)
콜백 함수란 처리되어야 하는 이벤트들을 순차적으로 넣어주는 방식이다. 함수 호출에서 다른 함수가 인자로 전달될 때, 인자로 전달 된 함수를 콜백 함수라고 부른다.
callBack1(function(result1) {
callBack2(result1, function(result2) {
callBack3(result2, function(result3) {
console.log(result3);
}, failureCallback);
}, failureCallback);
}, failureCallback);
위의 예제는 callBack3 함수를 호출 할 때 callBack2 를 인자로 전달하고, callBack2 함수를 호출 할때는 callBack1 를 인자로 전달하는 코드이다.
하지만 콜백 방식은 코드가 더 길어지고 복잡해질 경우 콜백 지옥 이라 불리는 단점을 가지고 있는데,,
- 가독성이 떨어진다.
→ 코드가 길어질 경우 어디서 어떤식으로 연결되었는지 한눈에 파악하기 어렵다. - 에러 해결, 디버깅, 문제분석, 유지보수가 어렵다.
이러한 콜백 지옥 문제를 해결하기 위해 ES6에서 비동기 흐름을 컨트롤하는 방법으로 Promise 객체가 등장한다.
2. Promise 객체
Promise 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다.
프로미스를 사용하면 비동기 메서드에서 마치 동기 메서드처럼 값을 반환할 수 있습니다. 다만 최종 결과를 반환하지는 않고, 대신 프로미스를 반환해서 미래의 어떤 시점에 결과를 제공합니다. MDN
Promise를 사용하여 비동기 작업이 (무조건 성공 또는 실패) 완료된 후의 결과 값을 받을 수 있다.
결과 값을 돌려받을 수 있기 때문에 이후 처리를 컨트롤 할 수 있게 된다.
Promise 상태 (State)
Pending(대기): 이행하거나 거부되지 않은 초기 상태
Fulfilled(이행): 연산이 성공적으로 완료됨
Rejected(거부): 연산이 실패함
Promise 생성
Promise는 함수를 인자로 받으며 해당 함수는 다시 resolve와 reject 2개의 함수를 인자로 받게 된다.
- resolve : 연산이 성공적으로 완료된 경우의 최종 결과를 반환
- reject : 연산이 실패한 경우 Error object를 반환
new Promise 로 생성된 인스턴스는 '객체'이기 때문에 변수로 할당하거나 함수의 인자로 사용가능하다.
Promise 사용
then과 catch 메소드를 사용해 인스턴스를 호출 한다.
resolve → then resolve되는 값, 성공적으로 완료된 결과 값은 then 메소드의 인자로 넘어간다.
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(222);
}, 1000);
});
promise
.then(resolve => console.log(resolve); //222
reject → catch reject 되는 값, 실패하게 되면 catch메소드의 인자로 넘어가 에러 핸들링 가능.
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
reject(404);
}, 1000);
});
promise
.then(resolve => console.log(resolve))
.catch(error => console.error(error)); // 404
then 메소드는 Promise 객체를 반환하기 때문에 then, catch메소드를 사용할 수 있다. then 메소드를 연속적으로 사용하면 Promise chaining이 가능하다.
Promise chaining
순차적으로 각각의 작업이 이전 단계 비동기 작업이 성공하고 나서 그 결과값을 이용하여 다음 비동기 작업을 실행해야 하는 경우에 Chaining을 이용하여 해결한다
- chaining을 할 때 then 메소드는 값을 바로 전달할 수도 있고, return으로 비동기인 프로미스를 전달할 수도 있다.
doSomething() // doSomething의 반환 값은 then 함수의 인자로 전달된다.
.then(res => doSomethingElse(result)) // then 함수의 인자로 전달
.then(res => doThirdThing(newResult)) // then 함수의 인자로 전달
.then(res => console.log(`Got the final result: ${finalResult}`)) // then 함수의 인자로 전달
.catch(err => console.error(err)); // 위의 Promise 객체들 중 하나라도 에러가 발생하면 catch 메소드로 넘어간다.
프로미스들을 연결할 경우 코드가 난잡해질 수 있는데 이때는 async·await을 활용할 수 있다.
3. async & await
async & await 은 Promise를 사용하지만 then, catch 메소드를 사용하여 컨트롤 하는 것이 아닌 동기적 코드처럼 반환 값을 변수에 할당하여 작성할 수 있게해주는 방식이다.
async는 promise의 코드를 깔끔하게 줄여주기 때문에 가독성을 높혀준다.
- function 앞에 async을 붙여준다.
- 비동기로 처리되는 부분 앞에 await만 붙여준다.
- ** await 뒤에 오는 부분은 반드시 promise를 반환해주어야 한다.
- ** async가 붙은 function도 promise를 반환해야 한다.
function asyncItem() {
return new Promise((resolve, reject) => {
const item = [1, 2, 3];
resolve(item);
});
}
async function logItems() {
const resultItem = await asyncItem();
console.log(resultItem);
}
- asyncItem() 함수는 프로미스 객체를 반환하는 함수이다.
- asyncItem() 함수 실행시 프로미스가 이행(Resolved)되며 결과 값은 items 배열이 된다.
- logItems() 함수를 실행하면 asyncItem() 함수의 결과 값인 items 배열이 resultItems 변수에 담긴다.
- 따라서, [1,2,3]이 출력.
await를 사용하지 않았다면 데이터를 받아온 시점에 콘솔을 출력할 수 있게 콜백 함수나 .then()등을 사용해야 했지만, async & await 문법으로 간단하게 작성이 가능하다.
async & await 예외 처리
Promise에서 예외 처리를 위해 .catch()를 이용했던 것처럼 async에서는 try / catch { } 를 사용한다.
async function test() {
try {
var user = await fetchUser();
if (user.id === 1) {
var todo = await fetchTodo();
console.log(todo.title);
}
} catch(err) {
console.log(error);
}
}
위의 코드를 실행하다가 발생한 네트워크 통신 오류뿐만 아니라 간단한 타입 오류 등의 일반적인 오류까지도 catch로 잡아낼 수 있다. 발견된 에러는 error 객체에 담기기 때문에 에러의 유형에 맞게 에러 코드를 처리해주시면 된다.
비동기 처리를 하는 세 가지 방법
1. 콜백 함수 사용
2. Promise
3. async & await
처음 배우는 문법은 항상 한번에 이해하기가 어렵다😢 정리를 하면서 이해하는 순간이 있고 정리하고 나서도 한참 후에야 이해가 되는 경우도 있으니.. Promise와 async & await은 중요한 문법이니 실제 코드를 여러번 작성하면서 완벽히 이해하도록 해봐야겠다.
'JavaScript' 카테고리의 다른 글
이벤트 처리하기 & 이벤트 종류 (0) | 2023.05.02 |
---|---|
배열 고차 함수 (filter / map / reduce) (0) | 2023.04.25 |
[객체 지향 프로그래밍]프로토타입 체인 (0) | 2023.03.16 |
[객체 지향 프로그래밍] 프로토타입과 클래스 (0) | 2023.03.15 |
객체 지향 프로그래밍(OOP)이란? (0) | 2023.03.15 |