우선 자바스크립트 언어 자체를 나는 비동기적 언어로 알고 있었다. 하지만 자바스크립트는 동기적 언어이고, 이 언어가 브라우저 엔진에서 작동할 때 비동기적으로 처리 된다는 사실을 알았다. 이러한 비동기적 처리는 event loop로 인해 가능한 것이다.
이에 대해서 좀 더 정리해보며 내가 가지고 있던 잘못된 개념도 정리해보려 한다.
우선 언어가 동기적으로 처리될 때와 비동기적으로 처리될 때 어떠한 차이가 있는 지에 대해서는 해당 링크를 참고하면 된다.
- Syncronous 동기 : 요청을 보낸 후 해당 요청의 응답을 받아야 다음 동작을 실행하는 방식
- Asynchronous 비동기 : 요청을 보낸 후 응답과 관계없이 다음 동작을 실행하는 방식
자바스크립트는 동기적이다.
- 결론부터 말하자면, 자바스크립트는 동기적이고, blocking이며, single-threaded한 언어이다. 각 스레드는 한 번에 하나의 작업만 순차적으로 수행할 수 있다. 일반적으로 멀티 스레드라 하면 동시에 여러 작업을 처리하는 것인데, 자바스크립트는 싱글스레드를 사용하기 때문에 기본적으로 동기적으로 코드를 처리한다.
- 사람들이 JavaScript가 비동기 언어라고 흔히 오해하는 이유는, 우리는Javascript가 비동기식으로 동작하도록 조작할 수 있기 때문이다.
그렇다면 우리가 자바스크립트를 비동기적 언어라고 오해하게 된 이유 중 하나인, 비동기적 동작의 핵심에 대해서 정리해보자.
비 동기로 동작하는 핵심요소는 자바스크립트 언어가 아니라 브라우저가 가지고 있다. (Node에서는 libuv 라이브러리 등)
브라우저는 Web APIs, Event Table, Callback Queue, Event Loop 등으로 구성되며 자바스크립트 코드가 실행될 때 브라우저와의 동작은 아래 그림을 참고할 수 있다.
- Heap: 메모리 할당이 발생하는 곳
- Call Stack : 실행된 코드의 환경을 저장하는 자료구조, 함수 호출 시 Call Stack에 push 됩니다.
- Web APIs: DOM, AJAX, setTimeout 등 브라우저가 제공하는 API
- Callback Queue: 이벤트 발생 시 실행해야 할 callback 함수가 Callback Queue에 추가.
- Event Loop2. Call Stack이 비어있을 경우, Callback queue에서 함수를 꺼내 Call Stack에 추가한다.
- 1. Call Stack과 Callback Queue를 감시.
자바스크립트 브라우저 동작 순서
1.console.log(‘first’)가 Call Stack에 추가(push)
2. console.log(‘first’)가 실행되어 화면에 출력한 뒤, Call Stack에서 제거(pop)
3.setTimeout(function cb() {..}) 이 Call Stack에 추가
4. setTimeout 함수가 실행되면서 Browser가 제공하는 timer Web API 를 호출. 그 후 Call Stack에서 제거.
5. console.log(‘third’)가 Call Stack에 추가
6. console.log(‘third’)가 실행되어 화면에 출력되고 Call Stack에서 제거
7. setTimeout 함수에 전달한 0ms 시간이 지난뒤 Callback으로 전달한 cb 함수가 Callback Queue에 추가.
8. Event Loop는 Call Stack이 비어있는 것을 확인하고 Callback Queue를 살펴본다. cb를 발견한 Event Loop는 Call Stack에 cb를 추가
9. cb 함수가 실행 되고 내부의 console.log(‘second’)가 Call Stack에 추가
10. console.log(‘second’)가 화면에 출력되고 Call Stack에서 제거
11. cb가 Call Stack에서 제거
Event Loop를 포함해 Browser의 구성요소 역할을 이해했다면, 자바스크립트 언어 자체가 비동기 특성을 제공하는게 아니라 Browser의 구성 요소들이 제공한다는 사실을 이해할 수 있다.
자바스크립트 코드에서 비동기 코드를 사용할 수 있기는 하지만 ES6가 나오기 전 까지는 자바스크립트에 비동기성을 표시할 수 있는 직접적인 방법이 없었다.
그러면 우리가 만든 프로그램의 코드를 실행하라고 JS엔진에게 명령하는 것은 누구일까? 실제로 JS엔진은 고립되어서 수행되는 것이 아니고 호스팅 환경 내에서 수행된다. 대부분의 개발자들에게 이것은 웹 브라우저 혹은 Node.js일 것이다. 이러한 모든 환경에서 적용되는 최대공약수는 호스팅 환경에 내장된 메커니즘인 이벤트루프이다.. 이것은 시간의 흐름에 따라 코드의 수행을 처리하며 그 때마다 JS엔진을 작동시키게된다. 따라서 이벤트루프라는 호스팅 환경 내의 메커니즘으로 인해, JS엔진에게 비동기적 수행을 명령하게 되고, JS가 비동기적으로 실행되게 되는 것이다.
ES6 Job Queue
ES6/ES2015 에서 소개된 Job Queue는 Callback Queue와 다른 Queue이며 Promise를 사용할 경우 Job Queue를 사용하게 된다.
Job Queue의 우선순위가 Callback Queue보다 높다. 따라서 Event Loop는 Call Stack이 비어있을 경우, Job Queue에서 기다리는 모든 작업을 처리하고 Callback Queue로 이동하게 된다.
JS 비동기 처리 방식
추가적으로 JS를 비동기적으로 실행하게 해주는 대표적인 방식으로 Callback / Promise / Async & Await 가 있다.
(드림코딩 유튜브 영상 참고)
- callback
- Promise
async 에서 await를 사용함으로써, 비동기적 처리에서 다시, 동기적으로 코드가 실행되도록 설정해줄 수 있는 것이다.
즉 위의 세가지 방식을 통해, 자바스크립트를 비동기적으로 처리하되, 그 처리되는 순서와 시점을 지정해 줄 수 있는 것이다.
이로 인해 나는 자바스크립트는 비동기적 언어이고, 위의 방식으로 처리 시점을 지정할 수 있다라고 단순하게 생각한 것이었는데, 더 깊이 들어가면 좀 더 복잡하게 개념이 얽혀있다고 볼 수 있다. 아래의 글은 async/await에 대해 정리한 어느 블로그 글을 참고한 것이다.
async 함수 선언을 통해 비동기 함수를 정의한다. 이렇게 생성된 함수는 AsyncFunction 객체를 반환하는데 이는 포함되어 있는 코드를 수행하는 비동기 함수를 나타낸다. 이렇게 만들어진 비동기 함수가 호출 되면 이것은 프로미스를 반환한다.
async 함수는 await 구문을 포함할 수 있는데 이를 이용하면 함수의 수행을 멈추고 프로미스의 이행 값이 넘어오기를 기다렸다가 async 함수의 수행을 계속해서 이어가다가 마지막에는 이행된 값을 반환하게 된다.
Reference
JS 비동기 핵심 _ 이벤트루프 & 비동기처리방식(콜백,promise, async&await)
'Programming Language > JS&TS' 카테고리의 다른 글
JS - 객체지향 프로토타입 (2) | 2021.02.04 |
---|---|
Javascript와 Node의 차이점을 정리해보자 (1) | 2021.01.21 |
JS 변수 선언 방식_ let, var, const의 차이 (0) | 2021.01.21 |
Typescript 이해하기 (0) | 2020.11.23 |
Ecma Script6문법 (0) | 2020.11.05 |