diff --git "a/Javascript/\353\271\204\353\217\231\352\270\260/\354\235\270\354\210\230_\353\271\204\353\217\231\352\270\260.md" "b/Javascript/\353\271\204\353\217\231\352\270\260/\354\235\270\354\210\230_\353\271\204\353\217\231\352\270\260.md" index d301e5f..f94c666 100644 --- "a/Javascript/\353\271\204\353\217\231\352\270\260/\354\235\270\354\210\230_\353\271\204\353\217\231\352\270\260.md" +++ "b/Javascript/\353\271\204\353\217\231\352\270\260/\354\235\270\354\210\230_\353\271\204\353\217\231\352\270\260.md" @@ -1 +1,313 @@ -## 비동기 +# 비동기 +`JS 엔진`, `런타임`, `이벤트루프`, `싱글스레드`, `동기/비동기`, `블로킹/넌블로킹`, `이벤트루프` +## 1. JS엔진과 런타임과 **이벤트 루프** +### 1-1. 자바스크립트와 웹 브라우저 + +``` +💬 '크롬 브라우저'는 'V8 엔진'이 탑제된 '자바스크립트 런타임'이다. +``` +**[자바스크립트]** + + - 자바스크립트는 **‘프로그래밍 언어’** 이다. + +**[자바스크립트 런타임]** + + - 런타임은 **'프로그래밍 언어가 구동되는 환경'** 을 말한다. + - 자바스크립트 런타임은, 자바스크립트가 구동되는 환경이다. + - 자바스크립트 런타임의 종류는 **‘웹 브라우저(크롬, 파이어폭스,...)’** 와 **‘Node.js’** 가 존재한다. + +**[V8 엔진]** + + - V8은 오픈 소스 자바스크립트 엔진 중 하나이다. + - 자바스크립트 및 *웹 어셈블리 엔진이다. + - 크롬 웹 브라우저 및 Node.js 에서 사용하고 있다. + - V8은 '**자바스크립트를 바이트 코드로 컴파일하고 실행하는 방식**' 을 사용한다. + +**[웹 어셈블리란]** + + **웹어셈블리(WebAssembly)란 C나 C++와 같은 프로그래밍 언어를 컴파일해서 어느 브라우저에서나 빠르게 실행되는 형식으로 바꿔주는 기술을 뜻한다.** + + > 보통 웹 애플리케이션 개발시에는 JavaScript 프로그래밍 언어를 사용해 동적인 부분을 개발하는데 C나 C++ 언어들에 비해서는 느리다. + > 게임이나 동영상 편집 등과 같은 고성능 웹 애플리케이션을 개발할 때 브라우저의 동작을 빠르게 하기 위해서 C나 C++와 같은 언어로 개발 할 수 있게 하는 것이다. + > 고성능 웹 애플리케이션 개발 시 자바스크립트와 같이 사용되고, 자바스크립트를 대체하는 것이 아니라 보완하는 기술이다. + +**[V8 엔진과 웹 브라우저]** + + - V8 엔진 자체에는 **하나의 힙(heap)과 하나의 콜스택(callStack)만이 존재**한다. + - setTimeout, DOM, AJAX와 같은 비동기 메소드는 웹 브라우저를 통해 처리한다. + - 웹 브라우저의 Web APIs 는 [**‘이벤트루프와 콜백 큐’**](###-1-4.-이벤트루프)를 가지고 있다. + +### 1-2. 자바스크립트는 싱글스레드이다 + +``` + 💬 자바스크립트는 '싱글스레드'이다. +``` + +**[스레드]** + + - 스레드의 사전적 의미는 한 가닥의 실이라는 뜻이다. + - 한 가지 작업을 실행하기 위해 순차적으로 실행한 코드를 실처럼 이어 놓았다고 해서 유래된 이름이다. + - 각 스레드는 한 번에 하나의 작업만 수행할 수 있다 + + - 각 작업은 순차적으로 실행된다. + - **`Task A —> Task B —> Task C`** + - **멀티 스레드를 지원하는 프로그래밍 언어는 '여러 코어'를 사용하여 여러 작업을 동시에 완료할 수도 있다.** + - **`Thread 1 : Task A —> Task B`** + - **`Thread 2 : Task C —> Task D`** + - **동시에 돌릴 수 있는 스레드 수는 컴퓨터에 있는 코어 갯수로 제한된다.** + +**[싱글 스레드]** + + - 싱글 스레드란 하나의 프로그램에서 동시에 하나의 코드만 실행할 수 있다는 뜻이다. + - 싱글 스레드란 코드가 실행되서 끝난 지점과 다음 코드의 시작 지점이 연결된 형태이다. + - **동기적 실행** + + - 싱글 스레드는 하나의 **'힙 영역과 하나의 콜 스택'** 을 가진다. + - 따라서 자바스크립트는 하나의 콜스택(call Stack)을 가지고, 한 번에 한 가지 일만을 수행할 수 있다. + - 코어가 여러개 있어도, 메인 쓰레드라는 단일 스레드에서만 작업을 수행할 수 있다. + +**[ 힙(Heap)과 콜스택(callStack) ]** + + - 힙은 **변수와 객체의 메모리 할당**에 사용되는 비정형 메모리이다. + - 콜스택은 **'함수가 실행되는 순서를 기억'** 하고, 이에 따라 **'코드를 읽는다.'** + +### 1-3. 자바스크립트에서의 동기와 비동기, 블로킹과 논블로킹 + +**[동기와 비동기]** + + - 동기와 비동기는, **여러 작업들이 동시에 실행될 수 있는가를 기준**으로 분류해볼 수 있습니다. + - `정의` 동기(Synchronous)는 선행 작업의 종료시점과, 후행 작업의 시작 시점이 일치하는 것을 말하며, 네트워크 관점에서는, 요청을 보낸 후, 응답(결과)를 받아야지만, 다음 동작을 진행할 수 있는 것을 말합니다. + - `장단점` 설계가 간단하고, 직관적인 장점이 있는 반면, 하나의 작업을 요청하고, 처리하는 과정에서 다른 작업을 진행하지 못하므로, 시스템의 전체적인 비효율이 발생할 수 있습니다. + + - `정의` 비동기(Asynchronouse)는 선행작업의 종료 시점과, 후행 작업의 시작 시점이 일치하지 않는 것을 말하며, 이에 따라 하나의 요청이 응답(결과)를 받아 끝나기 전에, 다른 여러 요청이 동시적으로 발생할 수 있는 것을 말합니다. + - `장단점` 시스템 자원을 효율적으로 사용할 수 있는 장점이 있는 반면, + 여러 작업이 동시에 처리되어, 해당 작업들의 순서성을 보장하기 어렵고 이에 따른 후속 처리 작업의 설계가 복잡할 수 있다는 단점을 가지고 있다. + +**[블로킹과 논블로킹]** + + - 블로킹과 논블로킹은 ‘하나의 동작의 속도와 상태’ 에 따라 분류해볼 수 있습니다. + - 블로킹은 정확한 정의는 존재하지 않지만, 대체로 느리게 동작되어, 스택에 오래동안 자리잡아, 다른 동작을 막고 있는 상태 + - 즉 **“CallStack이 멈추어 있는 상태” 를 블로킹 상태라고 할 수 있습니다.** + +**[비동기와 논블로킹]** + + ``` + 💬 동기적 처리의 문제점 때문에, 브라우저는 AJAX 요청등을 비동기적으로 실행한다. + ``` + + - **웹 브라우저의 Web APIs 는 비동기적으로 처리한다.** + - Web APIs의 setTimeout, DOM, AJAX 요청등이 대표적인 사례 + - 자바스크립트 자체는 비동기적 요청을 처리할 수 없기 때문에, 자바스크립트 런타임이 지원하는 API를 통해 비동기 요청을 처리한다. + - 이 비동기 처리는 웹 브라우저의 이벤트 루프와 콜백 큐를 통해 가능하다. + + ``` + 💬 '비동기 콜백 방식'을 통해, '싱글 스레드 프로그래밍 언어'에서 발생하는 '블로킹을 해결'할 수 있다. + ``` + - 싱글스레드는 스레드가 하나이기 때문에, 블로킹 상태에 직면할 확률이 높다. + - 스레드가 여러개라면, 동기적 요청이 여러개 있어도 논블로킹 상태일 수 있다. + - 비동기 콜백 방식으로, 당장 콜스택에 함수를 바로 실행하는 것이 아니라, + - 다른 곳에 쌓아 두어 동시에 요청을 처리하도록 하고(Web APIs에 위임하여, 비동기로 논블로킹 상태를 만듦) + - 요청이 완료된 순서대로 처리하도록 한다. + - “**이벤트 루프”** + - 이벤트 루프 + +### 1-4. 이벤트루프 +``` + 💬 이벤트루프란 “자바스크립트 런타임(브라우저)”에서 비동기 콜백을 만들어 처리한다. +``` + - 이벤트 루프는 ‘콜 스택’ 과 ‘콜백 큐'를 주시한다. + - **‘Web APIs’** 에서 실행되는 비동기 동작의 결과인 콜백함수들은 **‘콜백 큐(callback Queue)’** 에 쌓인다. + - 콜 스택이 비어 있다면, 큐의 첫번째 콜백을 스택에 쌓아 실행시킨다. + + - 이벤트루프의 콜백 큐는, 콜스택의 실행 순서와 상관 없이, 먼저 끝나는 작업 순서대로 쌓이게 된다. + - 비동기 작업의 순서를 제어할 수 없는 이벤트루프(WebAPIs)의 문제 발생 + + ![이벤트루프](https://i.ibb.co/VDmMfyd/image.png) + + +## 2. 콜백함수, Promise, Async-await +### 2-1. 콜백함수 + +**[1. 콜백함수란?]** +> 콜백(callback)이란 다른 함수(A)의 전달인자(argument)로 넘겨주는 함수(B)를 말한다. + + ```jsx + function A (someArg1, callback){ + ... + if(someArg1 === true){ + callback(); + } + } + + function B(){ + console.log('this is callback 함수 실행'); + } + + A(true, B) // 'this is callback 함수 실행' + ``` + +**[2. 콜백함수 예시]** + +- iterator + - `[1,2,3].map((el,idx) ⇒ + console.log(${idx}번째 요소는 ${el}이다.))` + - 고차함수는 callback함수를 받아, 순환하며 해당 callback을 실행하도록 하는 함수이다. +- eventHandler + - `$(’#app’).addEventListener(’click’, (e) ⇒ { console.log(’click 발생할 때 실행되는 콜백함수 이다. ’) )` + +**[3. 동기적 콜백과 비동기적 콜백의 차이]** + +- 동기적 콜백 + > 하나의 작업이 모두 종료된 이후에 새로운 작업을 실행하는 방식 (`블로킹 발생`) + + ```jsx + // 동기적으로 3초 마다 배열의 수가 출력된다. (블로킹을 느낄 수 있다.) + [1, 2, 3].forEach((num) => { + // 콘솔 한번 찍히려면 매번 3초씩 걸린다. + // 이 함수의 콜백 함수는 동기적으로 호출된다. + imCallbackFn(() => console.log(num), 3000); + }); + function imCallbackFn(callback) { + let start = Date.now(); + let now = start; + while( now - start < 3000 ) { + now = Date.now(); + } + callback(); + } + // 콘솔 출력까지 총 수행 시간 3 * 3 = 9초 + // 의도적으로 타이머의 기능이 필요하다면 동기적 콜백 호출이 필요할 수도 있다. + ``` + + - ⭐️ 동기적 콜백 방식은 블로킹을 발생하기 때문에, 의도하지 않는다면 비효율을 발생시킨다. 따라서 JS의 런타임(브라우저, node.js)가 제공하는 API를 통하여, 비동기적 콜백 호출을 통해 블로킹을 해결하고, 비효율을 해결할 수 있다. +- 비동기적 콜백 + > 새로운 요청과 이전 요청에 대한 처리를 동시에 진행하는 방식 + + ```jsx + // 비동기적으로 3초 이후 배열의 수가 출력된다. (논 블로킹을 느낄 수 있다.) + [1, 2, 3].forEach((num) => { + // 자바스크립트 런타임의 'Web API'인 'setTimeout' 함수를 사용함으로써 + // 3초 이후 콘솔이 모두 출력된다. + // 이 함수의 콜백 함수는 비동기적으로 호출된다. + setTimeout(() => console.log(num), 3000); + }); + // 콘솔 출력까지 총 수행 시간 = 3초 + // 블로킹을 해결하고 속도를 개선하려면 비동기적 콜백 호출을 이용한다. + ``` + +- 이어지는 질문 > 자바스크립트에서 동기/비동기 작업은 어떻게 처리되는가 ([이벤트루프, 싱글스레드](###-1-4.-이벤트루프)) + +**[4. 비동기 처리, 비동기 제어란]** + +- WebAPIs에서 사용가능한 비동기 함수는 아래와 같다. + - DOM의 이벤트 핸들러 함수 + - 타이머 관련 함수 (setTimeout, 애니메이션API) + - 서버에 대한 요청 (fetch, AJAX) +- 비동기 함수들은, 결과를 쉽게 예상할 수 없다는 치명적 단점을 가지고 있다. + - 각각의 task의 종료시점을 알 수 없기 때문에, 콜백함수의 순서를 제어할 필요가 있다. +- **비동기 제어**란, 비동기 함수가 가진 콜백함수들의 순서를 제어하는 것을 말한다. +- **자바스크립트의 비동기 처리**란 특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행하는 자바스크립트의 특성을 의미합니다. ([ref. josha](https://joshua1988.github.io/web-development/javascript/javascript-asynchronous-operation/)) + +**[5. 콜백을 통한 비동기 제어]** + +```jsx +function fn() { + setTimeout(() => { + console.log('하나'); + setTimeout(() => { + console.log('둘'); + setTimeout(() => { + console.log('셋'); + }, 0); + }, 0); + }, 0); +} +fn(); // 결과 순서 => '하나', '둘', '셋' +``` + +- 비동기 함수 내의 콜백함수를 통해, 특정 동작을 실행시킬 수 있다. + - 이 콜백함수안에 또 다른 비동기함수를 호출한다면, 여러 작업을 순차적으로 실행시킬 수 있다. + +**[문제점]** + +- 콜백 지옥 (순차성 보장하지못함) + - 콜백함수의 순서를 확정하기 위해, 콜백함수안에 반복적으로 비동기함수를 호출하는 작업의 결과로 들여쓰기가 깊어지며, 가독성을 헤치는 현상을 말한다. + - 가독성, 직관적 이해, 코드 수정을 어렵게 한다. + +- 에러처리가 어려움 + - setTimeout 함수는 콜백함수를 WebAPIs에 넘긴 직후, 콜스택에서 제거되기 때문이다. + - 따라서 1초 후 Error가 발생했을 때는, setTimeout함수는 콜스택에 존재하지 않고, 콜백함수를 호출한 함수가 setTimeout 함수가 아닌 것을 뜻한다. 따라서 호출자 방향으로 전파되는 에러(exeption)에 catch 되지 않는 것이다. + + ```jsx + try { + setTimeout(() => { throw new Error('Error!'); }, 1000); + } catch (e) { + console.log('에러를 캐치하지 못한다..'); + console.log(e); + } + ``` + +- 제어의 역전 문제 + - 제어의 역전은, 메인 프로그램에서 모든 함수를 통제하는 것이 아니라, 특정 하위 부분에 제어권을 넘겨주는 현상 + - 콜백함수를 인자로 가지는 비동기 함수에게 제어권을 넘겨주기 때문에 발생 + - 해당 비동기 함수가 제대로 처리되어 있지 않다면, 신뢰를 잃게 되고, 이를 보완하기 위해서 수많은 방어 로직을 만들어 재사용이 어렵게 된다. + +### 2-2. Promise + +``` + 💬 프로미스란 자바스크립트 비동기 처리에 사용되는 객체 +``` + +**[비동기 처리]** + +- **비동기 제어**란, 비동기 함수가 가진 콜백함수들의 순서를 제어하는 것을 말한다. +- **자바스크립트의 비동기 처리**란 특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행하는 자바스크립트의 특성을 의미합니다. ([ref. josha](https://joshua1988.github.io/web-development/javascript/javascript-asynchronous-operation/)) + +**[프로미스 객체]** + +- 비동기 작업을 가진 **“콜백함수”** 를 인자로 전달 받는다. +- 해당 콜백함수는 resolve와 reject 함수를 인자로 전달 받는다. (인자를 기본적으로 가지고 있는다.) + +**[프로미스 상태]** + +- 비동기 처리 작업의 수행결과에 따라 resolve, reject 함수를 호출한다. +- 프로미스는 resolve, reject함수 호출 여부에 따라 3가지 상태를 가지게 된다. + - pending (resolve, reject 가 호출되지 않음) + - fullfilled ( 비동기 처리 성공하여 resolve 함수 호출된 상태) + - rejected (비동기 처리 실팽하여, reject 함수 호출된 상태) + - reject 상태가 된 경우, `catch()` 를 통해 실패처리의 결과 값을 받을 수 있다. + - `reject(new Error(”some Error”)` ⇒ Error 객체를 넘겨 줌 + +**[에러 처리]** + +- `then()` 의 두번째 인자로 err를 처리하는 방법 + - resolve이후 수행되는 로직(체이닝 된 로직)에서 발생하는 error를 catch 할 수 없다. +- `catch()` 를 통해 err를 처리하는 방법 + +**[Promise.all]** + +- 전달받은 모든 프로미스를 병렬적으로 처리할 때 사용하는 메서드 + - 모든 프로미스의 처리가 끝날 때 까지 기다린 이후, 모든 처리결과를 한 번에 `resolve || reject` 한다. + - 모든 프로미스가 resolve일 경우, 처리 결과를 배열에 담아 resolve 하는 프로미스 객체를 반환한다. + - 이때 첫번째 프로미스부터 차례대로 담기기 때문에, **“처리 순서가 보장된다!”** + - 하나라도 프로미스가 reject될 경우, 가장 먼저 실패한 프로미스가 reject한 error를 catch 하게 된다. + +**[Promise.race]** + +- Promise.all가 반대로, 가장 먼저 처리된 프로미스가, 결과 배열에 먼저 담기게 된다. + +### 2-3. Async-await + +- Es2017에 등장 +- 프로미스의 then안에 프로미스가 연속해서 처리 될 경우 가독성 저하 문제 +- 비동기 작업이 필요한 함수 앞에 await 키워드를 붙여 사용 +- 비동기 작업의 결과를 기다렸다가 반환하게 됨 + - Promise객체인 경우 resolve된 값을 반환 +- 비동기작업들을 동기적 코드와 같이 보이도록 하여, 직관적인 이해 및 처리순서 보장 가능 장점 +## 참고 + - [https://sinsomi.tistory.com/entry/신입-개발자-면접-동기와-비동기-개념-초코더](https://sinsomi.tistory.com/entry/%EC%8B%A0%EC%9E%85-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A9%B4%EC%A0%91-%EB%8F%99%EA%B8%B0%EC%99%80-%EB%B9%84%EB%8F%99%EA%B8%B0-%EA%B0%9C%EB%85%90-%EC%B4%88%EC%BD%94%EB%8D%94) + - [https://hanamon.kr/javascript-런타임-작동-방식-비동기와-이벤트-루프/](https://hanamon.kr/javascript-%eb%9f%b0%ed%83%80%ec%9e%84-%ec%9e%91%eb%8f%99-%eb%b0%a9%ec%8b%9d-%eb%b9%84%eb%8f%99%ea%b8%b0%ec%99%80-%ec%9d%b4%eb%b2%a4%ed%8a%b8-%eb%a3%a8%ed%94%84/) + + - https://joshua1988.github.io/web-development/javascript/js-async-await/ + + - https://poiemaweb.com/es6-promise \ No newline at end of file