CS/프로그래밍

동기, 비동기

고래강이 2023. 12. 15. 21:47

개요

  • Javascript의 동작 방식
  • 동기, 비동기 방식
  • Javascript의 비동기처리

브라우저 환경 구조 (callback queue = task queue)

Javascript 동작 방식

Javascript는 함수를 호출했을 때 어떤 동작을 실행하는지 알아보자!
Javascript엔진은 단 하나의 실행 컨텍스트 스택을 갖는다.(한 번에 하나의 함수만 실행 가능, 싱글 스레드 방식)
  1. 자바스크립트 엔진이 함수 코드를 평가하고 함수 실행 컨텍스트를 생성한다.
  2. 생성된 실행 컨텍스트는 실행 컨텍스트 스택(Callstack)에 push되고 함수 코드가 실행된다.
  3. 실행이 종료되면 함수 실행 컨텍스트가 컨텍스트 스택(Callstack)에서 pop으로 제거된다.

이러한 자바스크립트 엔진이 시간이 걸리는 작업을 마주하게 되면 블로킹(작업 중단)이 발생한다.

 

테스크 큐와 이벤트 루프

싱글 스레드인 Javascript가 여러 스레드가 사용되는 환경(Node.js, 브라우저)과 연동하기 위해서 사용되는 장치가 이벤트 루프이다.

 

javascript 엔진은 크게 2가지 영역으로 구분 된다.

  • 콜 스택 (Callstack) : 소스코드 평가 과정에서 생성된 실행 컨텍스트가 추가되고 제거되는 실행컨텍스트 스택
  • 힙 (Heap) : 객체가 저장되는 메모리 공간, 콜 스택의 요소인 실행 컨텍스트는 힙에 저장된 객체를 참조한다. 객체는 원시 값과 달리 크기가 정해져 있지 않아 할당해야 할 메모리 공간의 크기를 런타임에 결정(동적 할당)해야 한다. 그러므로 힙은 구조화 되어있지 않다.

비동기 처리에서 소스코드의 평가와 실행을 제외한 모든 처리는 브라우저 또는 Node.js가 담당하고, 이를 위해 브라우저는 이벤트 루프와 테스크 큐를 제공한다. 

  • 테스크 큐 (task queue) : 비동기 함수의 콜백 함수 또는 이벤트 핸들러가 일시적으로 보관되는 영역
  • 이벤트 루프 (event roof) : 콜 스택에 현재 실행 중인 실행 컨텍스트가 있는지 테스크 큐에 대기 중인 함수가 있는지 반복해서 확인한다. 콜 스택이 비어있고 테스크 큐에 대기 중인 함수가 있다면 순차적(선입선출)으로 함수를 콜 스택으로 이동한다.

+심화내용

더보기

이벤트 루프)

기본적으로 테스크가 들어오길 기다렸다가 테스크가 들어오면 처리하고 없는 경우에는 잠들어 있는 구조

  • 처리해야 할 테스크가 있는 경우:
    • 먼저 들어온 테스크부터 순차적으로 처리
  • 없는 경우: 
    • 잠들어 있다가 새로운 테스크가 추가되면 다시 시작

엔진을 활성화하는 대표적인 태스크

  • 외부 스크립트 <script src="...">가 로드 될 때
  • 사용자가 마우스를 움직일 때 mousemove 이벤트와 이벤트 핸들러를 실행하는 것
  • setTimeout에 설정한 시간이 다 된 경우, 콜백 함수를 실행하는 경우
  • 기타 등등...
이벤트 루프 동작 방식

테스크는 하나의 집합을 이루어서 구성되며 차례대로 처리가 된다. 이때 테스크를 처리하는 과정에서 새로운 테스크가 추가 될 수도 있다.

그렇다면 테스크의 처리가 오래걸리는 상태에서 많은 양의 테스크가 추가된다면 어떻게 될까?

엔진은 특정 테스크를 처리하는 동안엔 렌더링이 일어나지 않는다. 그러므로 테스크 처리시간이 길어지게 됨에 따라서 발생한 사용자의 이벤트 등의 새로운 테스크를 처리하지 못하게 된다. 이럴 경우 응답 없음 페이지가 나타나게 되고 무한루프에 빠진 상태라고 보면 된다.

 

테스크 큐)

비동기처리를 한 promise함수와 setTimeout 중 무엇이 먼저 실행될까요? 규칙성이 있을까요 먼저 실행된 코드일까요 뭘까요???

 

매크로테스크)

 일반적으로 콜스택 작업이 끝난 후에 실행되는 작업
  • 콜스택의 작업이 끝나지 않으면 매크로테스크는 실행되지 않는다.
  • setTimeout을 비롯한 I/O 연산들이 있다.

 

마이크로테스크)

코드를 사용해서만 만들 수 있으며 주로 프로미스를 사용해서 만든다.
  • 먼저 들어온 작업을 먼저 실행한다.
  • 매크로테스크보다 실행우선순위가 높고 콜스택이 비어있을 경우 실행된다.
  • .then/catch/finally 핸들러 사용
  • await 사용
  • queueMicrotask(func) 표준 API 사용

Javascript 엔진은 매크로테스크 하나를 처리할 때마다 또 다른 매크로테스크나 렌더링 작업을 하기 전에 마이크로테스크 큐에 쌓인 마이크로테스크 전부를 처리한다

태스크의 동작방식

동기 VS 비동기

동기 방식 (Syncronous)

요청을 보냈을 때 응답이 돌아 온 뒤 다음 동작을 수행하는 방식
  • A작업이 마치기 전까지 B작업은 대기해야 한다.
  • 직렬로 배치되어 효율성이 떨어지지만 순서에 맞게 실행이 가능하다. 
console.log('first'); // first
console.log('second'); // second
console.log('third'); // third

 

비동기 방식 (Asynchronous)

동기와는 다르게 요청을 보냈을 때 응답을 기다리지 않고 다음 요청을 처리
  • 응답을 기다리지 않기에 효율성이 좋다
  • 병렬로 배치되어 먼저 실행된 요청이 끝나기 전에 더 나중에 실행된 요청이 끝날 수 있어 순서 보장이 안된다. 

 

콜백 함수

비동기 처리가 끝난 후 처리 결과를 알려주는 함수

 


Javascript의 비동기 처리

 

1. 콜백함수) 참고

다른 함수에 매개변수로 넘겨준 함수
/* 1. 기본예제 */
function a(callback){ 
   console.log('나는 a');
   callback(); // callback = console.log('나는 b')
}

function b(){
   console.log('나는 b');
}

a(b); 
// 결과 : 나는 a 나는 b

 

콜백지옥 : 콜백함수를 익명함수로 전달하는 과정에서 계속된 반복으로 들여쓰기 수준이 감당하기 힘들정도로 깊어지는 현상으로 가독성을 떨어뜨리고 코드 수정을 어렵게 만든다.

 

2. Promise) 참고

ES6에서 도입된 비동기 처리에 사용되는 객체
//Promise 선언
var _promise = function (param) {
	// return 값이 promise객체임을 알 수 다
	return new Promise(function (resolve, reject) {
		// 비동기를 표현하기 위해 setTimeout 함수를 사용 
		window.setTimeout(function () {
			// 파라메터가 참이면, 
			if (param) {
				resolve("해결 완료"); // 해결됨 
			}
			// 파라메터가 거짓이면, 
			else {
				reject(Error("실패!!")); // 실패 
			}
		}, 3000);
	});
};

//Promise 실행
_promise(true)
.then(function (text) {
	console.log(text); // 성공시
}, function (error) {
	console.error(error); // 실패시 
});

 

Promise의 상태

promise의 상태

  • pending : 아직 약속을 수행 중인 상태 (fulfiled or reject)
  • fulfiled : 약속이 지켜진 상태
  • rejected : 약속이 어떤 이유에서 못 지켜진 상태
  • settled : 약속이 지켜졌든 안지켜졌든 결론이 난 상태

 

3. Async/await) 참고

ES8에서 도입된 문법으로 깔끔한 비동기 코드 작성이 가능해졌다.
  • 개발자가 읽기 좋은 코드 작성이 가능해 짐
  • 콜백 깊이가 깊지 않을 때는 콜백함수나 promise의 사용이 나을 때도 있음
  • promise를 알아야 사용 가능
  • 상황에 따라서 잘 사용하자
const "변수명" = async() => {
    await someAsyncFunction(){...}
    await anotherAsyncFunction(){...}
}
  • async : 함수 이름 앞에 비동기를 사용하겠다는 선언
  • await : 호출 할 비동기 함수 앞에 사용하는 키워드
  • 항상 같이 사용되어야한다

try / catch / finally를 통한 예외처리가 가능하다.

 

프로미스 체이닝 : 콜백 지옥과는 다른 방식으로 나쁜 가독성을 보여주는 예시

 

 

 

 

 

 참고자료 : 블로그1 , 블로그2, 블로그3, 모던자바스크립트

'CS > 프로그래밍' 카테고리의 다른 글

props/state  (0) 2023.12.24
리액트의 라이프사이클  (0) 2023.12.16
Typescript  (0) 2023.12.08
버블링, 캡쳐링  (0) 2023.12.07
클로저  (1) 2023.12.07