Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

자바스크립트/비동기/insoo #38

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
314 changes: 313 additions & 1 deletion Javascript/비동기/인수_비동기.md
Original file line number Diff line number Diff line change
@@ -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