
자바스크립트는 다른 멀티스레드 프로그래밍 언어와 다른 방식으로 비동기 동작을 처리함
내부의 비동기 동작을 이해하기 위해서는 이벤트 루프 등의 개념을 알아야 한다!

현재 실행 중인 코드가 종료되기 전까지 다음 줄의 코드를 실행하지 않는 것
let a = 10;
console.log('a : ', a);
function foo(num) {
for (let i = 0; i < 10; ++i) {
console.log(num);
}
}
foo('num');
// a : 10
// num * 10

현재 실행 중인 코드가 종료되기 전에 다음 라인의 코드를 실행하는 것
let a = 10;
setTimeout(function callback() {
console.log('a: ', a);
}, 3000);
console.log('Finished');
// Finished
// a: 10
자바스크립트 엔진은 비동기 처리를 제공하지 않는다. 대신, 비동기 코드는 정해진 함수를 제공하여 활용할 수 있다. 이 함수들을 API라 한다.
비동기 API의 예시)
setTimeout,XMLHttpRequest,fetch등
node.js의 경우 파일 처리 API, 암호화 API 등을 제공한다
// 타이머 비동기 처리
setTimeout(() => console.log('타이머 끝'), 1000);
setInterval(() => console.log('인터벌 타이머'), 1000);
// 네트워크 처리
fetch('https://google.com')
.then(() => console.log('네트워크 요청 성공'))
.catch(() => console.log('네트워크 요청 실패'));

비동기 코드가 불러와지고, 브라우저의 경우 Web API 모듈에서 setTimeout의 delay 시간이 만료되면 Task queue에 콜백함수를 넣고, 코드가 실행된다. Job queue는 Promise나 애니메이션 프레임 같은 경우에 사용된다.
setTimeout(fn, 0)을 실행하면 어떻게 될까?
- setTimeout 함수의 delay 시간이 4ms 이하인 경우 최소 지연 시간인 4ms가 지정된다.
- setTimeout시 실행되면서 call stack에서 setTimeout이 제거
- 타이머가 만료되면 콜백함수 fn이 task queue에 들어간다.
- 이벤트루프가 call stack이 비었는지 확인하고, 비었으면 콜백 함수를 call stack에 넣는다.
- fn이 실행되고 콜스택에서 제거된다.
이때 task queue에 담긴 callback이 많거나 CPU-intensive한 작업을 수행하게 되면 병목현상이 일어나 queue 뒷단에 있는 함수들은 실행이 늦어지게 된다. 실행 시점이 중요한 콜백 함수들엔 문제가 될 수 있다.
그렇기 때문에 Node.js는 시간에 민감한 어플리케이션을 개발하기에는 적합하지 않다.
👩🏻💻정리!
- 비동기 코드를 처리하는 모듈은 자바스크립트 엔진 외부에 있다
- 이벤트 루프, 태스크 큐, 잡 큐 등으로 구성된다.
- API 모듈은 비동기 요청을 처리 후 태스크 큐에 콜백 함수를 넣는다.
- 자바스크립트 엔진은 콜 스택이 비워지면, 태스크 큐의 콜백 함수를 실행한다.
각 함수에 대한 데이터를 사용하기 위해 비동기적으로 데이터를 가져와야 한다고 가정
function getName(cb) {
setTimeout(() => {
cb("Elice");
}, 2000);
}
function getAge(cb) {
setTimeout(() => {
cb(6);
}, 2000);
}
function getAddress(cb) {
setTimeout(() => {
cb("Seoul");
}, 2000);
}
console.log를 한 번만 사용해야 한다면 어떻게 해야할까?
getName((name) => {
getAge((age) => {
getAddress((address) => {
console.log(name, age, address)
})
})
})
이렇게 콜백함수 안에 콜백함수를 반복 호출하면 name, age, address를 한꺼번에 접근할 수 있다. 비동기 함수가 3개 쓰이고 각 2초씩 걸리기 때문에 6초 뒤에 Elice 6 Seoul이라는 log가 나온다.
이런 식으로 비동기 함수가 많아지면, 이를 콜백 지옥이라고 부른다.
콜백 함수가 많아지면 함수가 복잡해지고 가독성이 떨어지며, 콜스택의 아래 방향으로 에러가 전파되기 때문에 에러처리가 곤란하다.
function getName() {
return new Promise((resolve) => {
setTimeout(() => {
resolve("Elice");
}, 2000);
})
}
function getAge() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(6);
}, 2000);
})
}
function getAddress() {
return new Promise((resolve) => {
setTimeout(() => {
resolve("Seoul");
}, 2000);
})
}
// promise
Promise
.all([getName(), getAge(), getAddress()])
.then((res) => {
const [name, age, address] = res;
console.log(name, age, address)
})
// async/await
(async () => {
const name = await getName();
const age = await getAge();
const address = await getAddress();
console.log(name, age, address);
})();
setTimeout(() => {
console.log('타임아웃1');
}, 0);
Promise.resolve().then(() => console.log('프로미스1'));
setTimeout(() => {
console.log('타임아웃2');
}, 0);
Promise.resolve().then(() => console.log('프로미스2'));
// 프로미스 1 프로미스 2
// 타임아웃 1 타임아웃 2
pending), 성공(resolved, fulfilled), 실패(rejected) 상태를 표현settled라고 한다.let promise = new Promise((resolve, reject) => {
if (Math.random() < 0.5) {
return reject('실패');
}
resolve(10);
});
new Promise(callback) 으로 생성let promise = new Promise((resolve, reject) => {
if (Math.random() < 0.5) {
return reject('실패');
}
resolve(10);
});
promise
.then((data) => console.log('성공 : ', data))
.catch((e) => console.log('실패 : ', e))
.finally(() => console.log('promise 종료'));
.then 메서드가 호출이 되고, 실패는 .catch 메서드가 호출된다. 또는 .then(성공fn, 실패fn) 으로 작성할 수 있다.finally() 메서드는 성공/실패 여부와 상관 없이 실행할 콜백함수를 넘긴다.promise
.then((data) => {
return fetchUser(data);
})
.then((user) => console.log('User : ', user))
.catch((e) => console.log('실패 : '), e);
Promise
.resolve(10)
.then(console.log)
Promise
.reject("Error")
.catch(console.log)
Promise.resolve는 성공한 Promise 바로 반환Promise.reject는 실패한 Promise 바로 반환Promise.all([promise1, promise2, promise3])
.then((values) => {
console.log('모두 성공 : ', values);
})
.catch((e) => {
console.log('하나라도 실패 : ', e);
});
promise의 꼬리에 꼬리를 무는 코드 작성 방식 또한 함수가 많아지면 가독성이 떨어지게 된다.
이를 보완하기 위해 async await이 등장했다.
async 함수 안에서 await을 통해 반환 값을 받아 올 수 있으며, 코드를 더 동기적으로 보이게 작성할 수 있다.
https://velog.io/@younngg1012/asyncawait