동기 방식은 작업을 순차적으로 처리한다. 한 작업이 완료되기 전까지 다음 작업은 대기한다. 이는 코드의 가독성을 높이고, 이해하기 쉬운 흐름을 제공하지만, 한 작업이 지연될 경우 전체 시스템의 성능에 영향을 줄 수 있다.
코드의 흐름을 쉽게 이해하고 추적할 수 있다.
디버깅이 비교적 간단하다.
리소스 활용이 비효율적일 수 있다.
사용자 경험에 부정적인 영향을 줄 수 있다(예: UI가 멈춰 보임).
비동기 방식에서는 작업이 백그라운드에서 실행되며, 완료되면 콜백 함수나 프로미스를 통해 결과를 처리한다. 이는 동시에 여러 작업을 처리할 수 있게 하여 애플리케이션의 반응성과 성능을 향상시킨다.
Javascript의 경우는 싱글스레드 기반이기 때문에 실제로 동시에 여러 작업을 처리하는 것은 아니다!! 이 부분은 아래에서 설명하겠다.
리소스를 효율적으로 사용하여 더 빠른 성능을 제공한다.
사용자 경험이 향상된다(예: 페이지 로딩 중에도 다른 작업 수행 가능).
코드의 복잡성이 증가할 수 있다.
콜백 지옥과 같은 문제를 유발할 수 있다.
결론부터 말하면 Javascript는 동시에 여러 작업을 실행할 수 없다. 싱글 스레드는 한번에 하나의 작업만을 실행하는 것을 의미한다. 그런데 비동기 식은 분명 응답을 기다리지 않고 다음 줄의 코드를 실행한다고 했는데, 싱글스레드인 Javascript에서는 어떻게 비동기식을 다룰 수 있는 것일까?
이를 알기 위해서는 자료구조의 기초인 Stack, 백그라운드, Queue에 대해서 이해할 필요가 있다. Stack은 Javascript 코드를 실행시키는 공간이고 Queue는 일종의 대기실이다. 백그라운드는 Javascript 외부 환경으로 설명하겠다. (Web Api)
Javascript에서 동기식으로 작성된 코드들은 Stack에 순서대로 쌓여서 실행된다. 하지만 비동기식의 코드들은 바로 Stack에 추가되는 것이 아니라, Javascript 외부의 백그라운드로 보내져 실행이 된다. 이 과정이 Stack에서 실행되는 것이 아니기 때문에 Stack에 쌓여진 다른 동기식들이 실행될 수 있다. 이후에 백그라운드에서 처리된 콜백(결과값)이, Queue라는 대기실로 보내지게 되며 Stack의 모든 코드들이 실행되어 빈 공간이 되었을 때 Stack으로 올려져 실행되게 되는 것이다.
위에서 비동기식이 Stack으로 바로 이동되는 것이 아니라, 백그라운드로 보내져 실행되고 Stack이 빈공간이 되면 Stack으로 올려져 실행된다는 것을 설명하였다. 둘다 뭔가가 실행되긴 하는데 이 둘의 차이는 무엇일까?
이해하기 쉽게 설명하자면 setTimeout, fetch와 같은 비동기식 그 자체는 백그라운드로 보내져 실행되고, 백그라운드로 보내진 비동기식의 콜백인 결과값이 Queue에서 대기하다가 Stack에서 실행되는 것이다.
간략한 순서
비동기식 호출!
백그라운드로 보내져 실행 (예: setTimeout의 대기 기능, fetch의 http 요청 기능)
실행된 비동기식의 콜백(결과값)이 Queue로 보내져 대기
Javascript가 Stack에 있는 코드들을 전부 실행시켜 Stack이 빈공간이 되면 Queue에서 대기하고 있던 콜백코드들이 Stack으로 이동되어 실행됨
이해하기 쉽게 예제 코드를 작성해봤다.
// 동기! Stack에서 바로 실행
console.log('첫번째!');
// 비동기! setTiemout은 백그라운드에서 실행된다
setTimeout(() => {
console.log('세번째!');
// 비동기식의 콜백인 콘솔이 2000ms후 Queue로 이동되어 Stack이 비었을 때 Stack에서 실행됨
}, 2000);
console.log('두번째!'); // 동기! Stack에서 바로 실행
"2000ms 지연시키는 기능"을 백그라운드에서 실행하는 것이다.
콜백(결과값)인 콘솔을 출력하는 것은 Stack에서 실행하는 것이다.
결과적으로 Javascript는 한번에 하나의 코드만 실행할 수 있는게 맞다.
비동기를 유연하게 구현하기 위해 비동기식들을 백그라운드로 보내 실행시키고 그 콜백(결과값)들을 Stack에서 동기적으로 실행하는 것이라 이해하면 쉬울것이다.