JavaScript 비동기 처리방식
자바스크립트는 Event Loop를 이용해서 비동기 방식으로 동시성을 지원한다.
이벤트 루프 이벤트 발생시에 호출되는 콜백 함수들을 태스크 큐에 전달하고 콜스택에 쌓여있는 함수가 없을 때, 태스크 큐에서 대기하고 있던 콜백 함수들을 콜스택에 넘겨준다.
JavaScript의 비동기 함수들
자바스크립트는 싱글스레드이기 때문에 한 번에 하나의 작업만 수행한다. 이를 해결하기 위해 비동기가 생겼다. 비동기란 특정 코드들의 처리가 끝나기 전에 다음 코드를 실행할 수 있는것을 뜻한다.
자바스크립트는 즉시 처리하지 못하는 이벤트들을 이벤트 루프에 모아놓고, 먼저 처리해야 하는 이벤트를 실행한다.
가장 대표적인 비동기처리 사례는 setTimeout()으로 일정시간 뒤에 함수를 실행시킨다.
비동기 방식
자바스크립트에는 콜백함수, Promise, async & await 이렇게 크게 3가지 방식이 있다.
1. 콜백함수 (Callback)
자바스크립트는 함수도 하나의 자료형이므로 매개변수로 전달할 수 있다. 다른코드의 인수로서 넘겨주는 실행 가능한 코드를 콜백함수라 말한다. 필요에 따라 즉시 실행 또는 나중에 실행 가능하다.
콜백 함수는 비동기로 함수의 매개변수에 다른 콜백함수가 중첩되어 사용되면 보기에 굉장히 어렵고 유지보수도 힘들다.
function callThreeTimes(callback) {
for(let i=0; i<3; i++) {
callback(i);
}
}
function print(i) {
console.log(`${i}번째 호출`)
}
callThreeTimes(print)
//실행결과
//0번째 호출
//1번째 호출
//2번째 호출
callThreeTimes()함수는 함수를 매개변수로 받아 해당 함수를 3번 호출함. callThreeTimes() 함수의 callback 매개변수에 print() 함수를 전달했다. 그리고 callThreeTimes() 내부에서는 callback(i)로 함수를 호출하고 있다. 따라서 매개변수로 전달했던 print()함수가 print(0), print(1), print(2)로 차례로 호출되어 실행 결과와 같은 결과를 냄.
또는 익명 함수 사용하기
function callThreeTimes(callback){
for (let i=0; i<3; i++) {
callback(i)
}
}
callThreeTimes(function (i){
console.log(`${i}번째 함수 호출`)
})
//실행 결과는 위와 같음
콜백 함수를 활용하는 함수 1 : forEach()
const numbers=[23, 52, 10, 3, 7]
numbers.forEach(function (value, index, array) {
console.log(`${index}번째 요소 : ${value}`)
})
//실행 결과
//0번째 요소 : 23
//1번째 요소 : 52
//2번째 요소 : 10
//3번째 요소 : 3
//4번째 요소 : 7
콜백 함수를 활용하는 함수 2 : map()
let numbers = [2, 3, 33, 11, 55]
numbers = numbers.map(function(value, index, array) {
return value*value
})
numbers.forEach(console.log)
//결과
//4 0 (5) [4, 9, 1089, 121, 3025]
//9 1 (5) [4, 9, 1089, 121, 3025]
//1089 2 (5) [4, 9, 1089, 121, 3025]
//121 3 (5) [4, 9, 1089, 121, 3025]
//3025 4 (5) [4, 9, 1089, 121, 3025]
//undefined
콜백 함수를 활용하는 함수 filter()
filter() 메소드도 배열이 갖고 있는 함수로, filter() 메소드는 콜백 함수에서 리턴하는 값이 true인 것들만 모아서 새로운 배열을 만드는 함수이다.
const numbers=[0,1,2,3,4,5]
const eventNumbers = numbers.filter(function (value) {
return value % 2 === 0
})
console.log(`원래배열: ${numbers}`)
console.log(`짝수만 추출: ${evenNumbers}`)
// 실행 결과
// 원래배열: 0,1,2,3,4,5
// 짝수만 추출: 0,2,4
2. Promise : 콜백 문제를 해결하기 위해 Promise 가 도입됨.
콜백 문제를 해결하기 위해 promise가 도입되었다. promise는 특정 시점에 어떠한 결과값을 반환 하겠다는 약속으로 new Promise()로 생성가능하며, 종료시 3가지 상태를 가짐.
pending: 이행하거나 거부되지 않은 초기 상태
fulfilled: 완료되어 프로미스가 결과 값을 반환한 상태
rejected: 실패하거나 오류가 발생한 상태
promise는 resolve와 reject 2개의 인자를 받으며, 비동기 처리가 성공하면 resolve, 실패시 reject 호출
promise는 .then을 이용한 체이닝이 가능하다.
에러처리는 catch()를 사용한다 -> 그러나 중첩으로 인해 콜백과 동일한 hell이 발생할 수 있다.
Q. 어떤 것이 먼저 실행될까?
console.log('hi')
setTimeout(function() {
console.log('0.1')
}, 100)
Promise.resolve()
.then(function() {
console.log('first')
})
.then(function() {
console.log('second')
})
setTimeout(function() {
console.log('0')
}, 0)
console.log('end')
결과는
hi
end
first
second
0
0.1
setTimeout()과 Promise는 모두 비동기 함수이지만, setTimeout은 태스크 큐지만 Promise는 비동기 중에서도 먼저 실행되는 마이크로 태스크 큐에 들어있기 때문에 먼저 실행이 된다. 이벤트 루프는 콜 스택이 비면 먼저 마이크로태스크 큐에서 대기하고있는 함수를 실행 하고, 이후에 태스크큐에서 대기하고있는 함수를 가져와 실행한다.
3. async await
Promise의 메서드 체이닝을 더 깔끔한 코드로 작성할 수 있게끔 만들어진 것이다. 콜백 hell을 없애 줄 수 있다.
async await 는 try 와 catch 문으로 성공, 에러 여부를 감지할 수 있다.
Promise
const promise = new Promise ((res, rej) => {
console.log('first')
setTimeout(()=>{
res('2초 후')
}, 2000)
console.log('end of function')
})
promise.then(res => console.log(res)).catch(err => console.error(err))
//결과
//first
//end of function
//Promise {<Pending>}
//2초 후
async await
const asyncFunc = async () => {
try{
console.log('first')
await setTimeout(()=> console.log('2초 후'),2000)
} catch (err){
console.log(err)
}
console.log('end of function')
}
asyncFunc()
같은 비동기 함수임에도 async await을 사용하면 코드를 더 깔끔하게 작성할 수 있다.
Reference
https://www.howdy-mj.me/javascript/async
'공부 정리' 카테고리의 다른 글
22.11.09 : webpack (0) | 2022.11.09 |
---|---|
22.11.08 : TCP, UDP (0) | 2022.11.08 |
22.11.07 : DOM (Virtual DOM vs Real DOM) (0) | 2022.11.07 |
22.11.05 : Redux란 무엇인가요? 왜 사용하시나요? (0) | 2022.11.05 |
22.11.04 : 상태관리 하는 이유, 평소에 state 관리는 어떻게 하시나요? (0) | 2022.11.04 |