JavaScript 동작
- 자바 스크립트는 싱글스레드로 동작
- 메인스레드 하나로 구성됨(하나의 호출 스택 사용) -> 한번에 하나의 작업만 수행할 수 있음
- 작업이 차례대로 실행됨 -> 하나의 작업이 끝날 때 까지 또 다른 작업을 실행하지 않음
- 호출 스택에 쌓인 함수/코드를 위에서부터 아래로 실행
- 하나의 작업이 끝나면 바로 pop하고 아래의 코드 실행
- 즉, JS는 런타임에서 자체적으로 비동기 API를 지원하지 않음!
- 동시성을 보장하는 비동기, non-blocking 작업은 JS 엔진을 구동하는 런타임 환경(브라우저/Nodejs)에서 지원
- 즉, JS는 코드를 그대로 실행만 하고 런타임 환경에서 이벤트를 스케줄 해주고 비동기 작업을 처리
JavaScript 런타임
- JS는 런타임에서 메모리힙과 콜스택으로 구성됨
- 런타임 : 특정 언어로 만든 프로그램들을 실행할 수 있는 환경(Node.js/브라우저)
- 메모리힙 : 메모리 할당을 담당, 코드에 선언된 함수를 저장
- 콜스택 : 코드를 호출하며 스택으로 쌓거나 Web API로 보냄, LIFO 방식(나중에 들어온 함수부터 처리)
-
- JS 엔진 밖에서도 JS에 관여하는 요소들이 있다. Wep API, Task Queue, Event Loop 등을 말한다.
- Wep API : 브라우저에 의해 제공되며, DOM, AJAX, setTime 등의 비동기 작업을 수행할 수 있게 해줌, call stack에서 함수가 web API를 실행하면 스택에서 pop 되며, Web API의 콜백함수는 큐에 저장됨
- Event loop : Call Stack이 비면 Task queue의 콜백함수를 Call stack에 넣는 역할을 함
- callback queue : 콜백함수가 저장. Queue이기 때문에 먼저 들어온것이 먼저 실행됨(FIFO)
- Microtask Queue : promise나 Mutation Observer의 콜백이 들어옴. Task Queue보다 우선순위가 높음, Queue가 빌 때까지 event loop가 실행됨
- Task Queue : 일반적인 콜백함수가 들어옴, Microtask Queue보다 우선순위가 낮고, 가장 앞에 있는 콜백함수만 Call stack에 들어감
비동기 동작 원리
Blocking VS Non-Blocking I/O
- Blocking : 말 그대로 무언가에 막혀 작업이 진행되지 않는 것을 말한다
- 무한루프, 잘못 설계된 너무 복잡한 로직/계산이 있는 코드, HTTP 요청
- Non - Blocking : 위의 경우중 http 요청작업에서 일어난다. 시간이 오래 걸릴 것 같은 동작들은 callstack에서 빠르게 빼버리고 JS callStack이 아닌 다른 환경에서 동작을 하게 하는 것
- 코드가 호출 스택에 쌓이고, 실행되면 , JS의 엔진은 비동기 작업을 Web API 에게 넘김
- Web API는 해당 작업을 수행, 콜백함수를 이벤트 루프를 통해 콜백 큐에게 넘겨줌
- 이벤트 루프는 콜스택에 쌓인 함수가 없을 때, 콜백 큐에 대기하던 콜백함수를 콜스택으로 넘겨줌
- 콜스택에 쌓인 콜백함수를 실행, 콜스택에서 제거함
- 하지만! 비동기와 동기를 구분하는 기준과 block/non-block을 구분하는 기준은 다름!
비동기 동작
- 동기 작업 : 작업들의 시작/종료 시점 및 순서를 결정할 수 있는 작업
- 비동기작업 : 여러 작업들 중 일부 작업들의 시작 종료 시점을 알 수 없는 작업들을 비동기 작업이라 함
이벤트 루프
- call stack에서 비동기 callback 함수가 호출
- 해당되는 비동기 callback 함수가 Wep API(background)로 이동됨, background는 call stack이 실행되는 스레드와는 독립적으로 동작
- 비동기 작업이 마무리 되면 내부함수를 task queue에 전달
- taske queue에 전달된 내부 함수는 call stack 이 빌 때 까지 기다림
- 독립적으로 실행되는 call stack의 모든 함수가 종료(anonymous까지 종료되면)
- 이 때 task quere에 있던 내부함수를 call stack으로 올려 내부함수를 수행
callback 함수
- 다른 함수의 파라미터로 이용되는 함수
- 어떠한 이벤트에 의해 호출되는 함수
- 코드를 통해 명시적으로 호출하는 것이 아님. 함수를 등록하기만 하고 어떤 이벤트가 발생하거나 특정 시점에 도달했을 때 시스템에서 호출하는 함수를 말함
- 가독성을 떨어트리고 디버깅을 어렵게함
Promise
- callback 함수의 단점을 극복하기 위해 등장
- 비동기 메소드에서 마치 동기 메소드처럼 값을 반환 할 수 있음
- 실제 값을 반환하지는 않고, 프로미스 객체를 반환해서 미래에 값이 반환되는 시점에 결과를 제공
- Promise 객체 : 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다
- 대기(pending) : 이행되거나 거부되지 않은 초기 상태
- 이행(fulfilled) : 연산이 성공적으로 완료
- 거부(rejected) : 연산 실패
- then : 프로미스를 잘 이행 해 결과값을 받으면 처리해줄 수 있음(프로미스 인스턴스의 프로토타입)
- catch : 실패한 이유를 받음(프로미스 인스턴스의 프로토타입)
- finally : 프로미스가 처리된 후(이행/거부된 후) 실행되어 원하는 처리를 할 수 있음(프로미스 인스턴스의 프로토타입)
- Promise의 method
- all : 여러 프로미스를 묶을 때 사용
- race
- reject : 주어진 이유로 거부할 수 있는 프로미스 객체를 반환
- reslove : 주어진 값으로 이행하는 프로미스 객체를 반환
- 프로미스 체이닝 : then() 메소드와 catch() 메소드가 체이닝 될 수 있음. 연쇄적으로 then 메소드를 호출해 여러 처리가 가능해짐
async/await
- 사용목적 : 여러 프로미스의 동작을 동기스럽게 사용할 수 있게 해주는 것
- async 함수는 항상 asyncFunction(프로미스) 객체를 반환하는 비동기 함수, 명시적으로 프로미스가 아니라면 암묵적으로 프로미스로 감싸짐
//default async function 함수명(){ await 비동기처리_메서드명(); } //예외처리시 asynch function getFirstIser(){ try{ let users = await getUsers(); return user[0].name; }catch (err){ retrun{ name: 'default user' }; } }
📖reference
https://blog.sessionstack.com/how-does-javascript-actually-work-part-1-b0bacc073cf
https://medium.com/codex/javascript-event-loop-callback-queue-f15a4dd7f32b
https://donologue.tistory.com/382
https://gisele-dev.tistory.com/m/24
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
'Programming > JavaScript' 카테고리의 다른 글
변수, 함수, this, 생성자 (0) | 2022.11.14 |
---|
댓글