자바스크립트의 동기 비동기
자바스크립트는 단일 스레드 프로그래밍 언어로 단일 호출 스택이 있어 한번에 하나의 일을 처리한다. 그리고 동기적으로 작동한다. 즉, 각 코드 블록이 순차적으로 실행된다.
하지만 다음과 같은 코드의 경우 비동기적으로 작동한다.
console.log("Hello");
setTimeout(() => {
console.log("jwoo");
}, 1000);
console.log("World");
// 만약 모두 동기적으로 작동했다면
Hello
jwoo // 1초 기다린후
World
// 실제 결과
Hello
World
jwoo
setTimeout 함수는 비동기적으로 동작하기 때문에 setTimeout 행에서 작업이 완료될 때까지 기다리지 않고 바로 다음 행을 수행한다.


싱글 스레드인 자바스크립트에서 이러한 비동기 작업이 가능한 이유는 자바스크립트가 독립적으로 실행되지 않고 웹브라우저나 NodeJS 같은 멀티 스레드 환경에서 실행되기 때문이다. 즉, 동시성을 보장하는 비동기, 논 블록킹 작업들은 자바스크립트 엔진을 구동하는 웹 브라우저, NodeJS와 같은 런타임 환경에서 담당한다.
하지만 코드를 짜다보면 비동기 함수들의 결괏값을 기다려야 하는 경우가 있는데, 그럴 때 쓰이는 것이 callback, promise, async/await 이다.
비동기 처리 방식 알아보기 (Promise, Callback, Async, Await)
Callback
콜백 함수는 이름 그대로 나중에 호출되는 함수를 의미하는데, 다른 함수의 인자로 전달받아 그 함수의 내부에서 실행된다. 그러면 함수 호출 스택에 따라 동기적으로 실행된다. 어떤 이벤트에 의해 호출되는 함수라고도 하며, 아래는 printImmediately 이벤트 함수가 callBackFunction 이라는 콜백 함수를 호출하는 코드이다.
function printImmediately(callBackFunction) {
callBackFunction()
}
printImmediately(()=>console.log('synchronous callback'))
콜백 지옥
콜백 함수에는 다시 콜백 함수를 넣어 실행 순서를 제어할 수 있다. A를 하고 A가 완료될 때까지 기다렸다가 B를 수행하고, B가 완료될 때까지 기다렸다가 C를 수행하고.. 이렇게 A 함수 안에 B 콜백 함수, B 콜백 함수 안에 C 콜백 함수를 계속 중첩하여 넣을 수 있다. 이렇게 끊임없이 콜백 함수를 잇다 보면 코드의 들여 쓰기 수준이 감당하기 힘들어질 뿐만 아니라 디버깅이나 에러처리에 어려움을 겪을 수 있는데, 이러한 상황을 콜백 지옥이라고 한다.

콜백 지옥을 개선하는 방법은 http://callbackhell.com/
Promise
프로미스는 비동기 작업의 최종 성공 또는 실패를 나타내는 객체이다. Callback의 단점을 해결하기 위해 ES6에서 지원한다. Promise 생성자로 만들 수 있다.
const myPromise = new Promise((resolve, reject) => {
// 코드 작성
// 프로미스의 성공을 알리기 위해서는 resolve,
// 프로미스의 실패를 알리기 위해서는 reject를 호출
})
Promise의 3가지 상태
- 대기(pending) : 이행하거나 거부되지 않은 초기 상태, 즉 약속된 결과 값이 반환되지 않은 상태
- 이행(fulfilled) : 연산이 성공적으로 완료된 상태
- 거부(rejected) : 연산이 실패한 상태
프로미스 객체는 반드시 위 3가지 상태 중 한 가지 상태를 가지며, 프로미스의 내부 객체(속성)이므로 직접 접근을 하지 않고 then과 catch를 통해 접근해야 한다. 프로미스가 성공할 때의 값을 얻는 데에는 .then()을, 실패할 때 오류처리는 .catch()를 사용한다. 만약 프로미스가 fulfilled나 rejected 상태를 가지게 되면 그 상태를 바꿀 수 없다.
// 성공
const myPromise = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve('success!');
}, 2000);
})
myPromise.then(data => {
console.log(data);
})
.catch(error => {
console.log(error);
})
/* 2초가 지난 뒤 success! 출력 */
// 실패
const myPromise = new Promise((resolve, reject) => {
setTimeout(()=>{
reject(new Error('this is error message'));
}, 2000);
})
myPromise.then(data => {
console.log(data);
})
.catch(error => {
console.log(error);
})
/* 2초가 지난 뒤 this is error message 출력 */
Promise Chaining
then과 catch의 메서드 반환값은 또 다른 프로미스를 객체를 반환하기 때문에 Chaining이 가능하다. 즉, 이전 프로미스에서 반환된 것을 후속 프로미스의 기반으로 사용하여 프로미스를 계속 연결할 수 있다. 기능은 비슷하지만 코드는 콜백 지옥보다 간결해진다.
const successPromise = new Promise((resolve, reject) => {
setTimeout(function () {
resolve("Success");
}, 3000);
});
successPromise
.then((value) => `${value} is`) // ${value} is를 결과 값으로 가진 Promise 생성
.then((secondValue) => `${value} not`))
.then((thirdValue) => console.log(thirdValue + " impossible"))
.catch((error) => {
errorHandling(error);
return "again?"; // catch 메소드 이후에도 체이닝 가능.
})
.then((lastValue) => console.log(lastValue));
.finally(() => console.log("chain end"));
- finally() 는 프로미스의 성공과 실패에 관계없이 실행되는 함수이다.
async/await
async/await 는 ES8에서 추가로 도입된 새로운 프로미스 작업방식이다. Promise 결괏값을 then , catch에서 다루는 게 아니라 변수에 담아 사용할 수 있다는 장점이 있다.
함수 앞에 anync 키워드를 만들면 비동기 함수를 만들 수 있으며, 해당 키워드는 자바스크립트에게 항상 프로미스를 반환하도록 지시한다. 만약 함수가 프로미스가 아닌 값을 반환하면, 반환 값을 자동으로 프로미스로 감싼 후에 반환한다. await 키워드는 비동기 함수 내에서만 작동한다. 프로미스가 결과를 반환할 때까지 기다리도록 자바스크립트에 지시하는 키워드이다.
function walk(amount) {
return new Promise((resolve, reject) => {
if (amount < 500) {
reject(new Error('the value is too small'));
}
setTimeout(() => resolve(`you walked for ${amount}ms`), amount);
});
}
async function go() {
const res1 = await walk(500);
console.log(res1);
const res2 = await walk(1000);
console.log(res2);
const res3 = await walk(300);
console.log(res3);
}
async function go2() {
try {
const res1 = await walk(500);
console.log(res1);
const res2 = await walk(1000);
console.log(res2);
const res3 = await walk(300);
console.log(res3);
} catch (error) {
console.log(error);
}
}
go2();
go();
you walked for 500ms
you walked for 1000ms
Error: the value is too small
you walked for 500ms
you walked for 1000ms
Uncaught (in promise) Error: the value is too small
비동기 프로그래밍(콜백, Promise, async/await)
'자바스크립트' 카테고리의 다른 글
| 클로저 (private 흉내) (0) | 2023.03.21 |
|---|---|
| this (0) | 2023.03.17 |
| 이벤트 위임 (0) | 2023.03.11 |
| 자바스크립트의 배열 (0) | 2023.03.08 |
| Hoisting과 Temporal Dead Zone (0) | 2023.03.06 |