JS 제어 흐름, 비동기

강정우·2022년 10월 12일
0

JavaScript

목록 보기
21/53
post-thumbnail

JS 제어흐름

  • JS는 다른 멀티 스레드 프로그래밍 언어(JAVA, C++)와 다른 방식으로 비동기 동작을 처리한다.
    즉, 만약 setTimeOut같은 함수를 만났다면 밑에 코드를 쭉쭉 실행하고 나중에 시간에 맞춰 다시 돌아온다는 뜻이다.
    그래서 JS는 비동기 제어흐름을 사용자에게 제공을 하는데 이는 사용자로 하여금 따라가기 힘들게 할 수 있다.
    따라서 JS는 비동기 흐름을 이해할 수 있어야 한다.
  • 처음 JS를 접하는 경우, 동작에 대한 정확한 이해가 없으면 코드의 흐름을 따라가기 어렵다.
  • JS내부의 비동기 동작을 이해하기 위해서는 이벤트 루프 등의 개념을 알아야 한다.

JS engine

  • JS engine은 하나의 메인 스레드로 구성된다.
  • 메인 스레드는 코드를 읽어 한 줄씩 실행한다.
  • 브라우저 환경에서는 유저 이벤트를 처리하고 화면을 그린다.
  • JS에서 setTimeOut이 돌아간다면 JS engine에서 돌아가는 것이 아닌 비동기 API환경에 함수를 올려놓는다. 그리고 시간이 되면 task Queue에서 실행하라고 알림이 간다. 그럼 task Queue함수의 종료의 콜백으로 해당 함수(여기서는 setTimeOut)를 가져와서 실행을 한다. 이로써 한개의 메인 쓰레드만으로도 여러일이 가능하다
  • 반면 JAVA는 새로운 쓰레드가 생성되고 이는 10초동안 아무것도 하지 않는다.

동기적 제어 흐름

  • 동기적 제어 흐름은 현재 실행중인 코드가 종료되기 전까지 다음 줄의 코드를 실행하지 않는 것을 의미한다.
  • 분기문, 반복문, 함수 호출 등이 동기적으로 실행된다.
  • 코드의 흐름과 실제 제어 흐름이 동일하다.
  • 싱글 스레드 환경에서 메인 스레드를 긴 시간 점유하면, 프로그램을 멈추게 한다. 따라서 JS같은 싱글 쓰레드는 한무 roop에 걸리지 않도록 주의해야한다. 즉, 동기처리에서 메인 쓰레드를 점유하는 시간이 길어지면 안 되기에 동기와 비동기를 정확하게 알고 시간이 오래걸리는 일들을 비동기 적으로 처리할 줄 알아야 한다.
  • 동기적 제어흐름의 코드 예시 :
// 선언문
let a = 10

// console.log
colsole.log("a :", a)

// 반복문
function foo(num){
	for (let i = 0; i < 10; ++i) {
    	console.log(num)
    }
}

// 함수호출 코드
foo(num)

  • 만약 서비스가 이렇게 이루어 진다면 서버를 갔다가 와서 결과값을 반환받을 때까지 아무것도 할 수 없게 되고 이는 이용자가 늘어나면 서비스가 중지될 수 있다.

비동기적 제어 흐름

  • 비동기적 제어 흐름은 현재 실행중인 코드가 종료되기 전에 다음 라인의 코드를 실행하는 것을 의미한다.
  • 포르미스, 콜백함수를 호출하는 함수 등은 비동기적으로 실행된다.
  • 코드 흐름과 실제 제어 흐름이 다르다.
  • 비동기 작업을 기다리는 동안 메인 스레드는 다른 작업을 처리한다.
let a = 10

setTimeout(function callback(){
	console.log("a :", a)
}, 3000)

console.log("Finished")
  • 사람이 눈으로 읽을 땐 a가 나오고 Finished가 나올것 같지만 사실 반대이다.

이벤트 루프

  • JS engine은 비동기 처리를 제공하지 않는다. 즉, 이벤트 루프는 자바스크립트 엔진 외부에 존재한다.
    node.js 환경의 경우 libuv 라는 모듈에서 비동기 처리를 담당한다
  • 대신, 비동기 코드는 정해진 함수를 제공하여 활용할 수 있다.
  • 이 함수들을 API라고 한다.
  • 비동기 API의 예시로, setTimeout, XMLHttpRequest, fetch 등의 Web API가 있다.
  • node.js의 경우 파일 처림 API, 암호화 API등을 제공한다.
// 타이머 비동기 처리
setTimeOut(() => console.log("타이머 끝"), 1000)
setInterval(() => console.log("인터벌 타이버"), 1000)

// 네트워크 처리
fetch("https://google.com")
	.then(() => console.log("네트워크 요청 성공."))
    .catch(() => console.log("네티워크 요청 실패."))

비동기 처리 모델

  • Task queue는 Call stack에서 setTimeOut()을 만나면 WEB API모듈로 보내고 여기서 설정된 시간이 지나면 Task queue로 넘어가도록 설정이 되어있다.
    Job queue는 Promise API나 Animation Frame에서 사용됨
  • 그럼 Event Loop는 뭐냐? 바로 Call stack의 함수를 모두 실행했다고 하면 Event Loop가 Task queue와 Job queue를 돌아다니면서 남은 함수가 있나 확인한다.
  • 동기처럼 위에서 아래로 context path가 실행되면 call stack에 함수들이 쌓임 그동안 비동기들은 Task queue나 Job queue에 저장된다 그 후 call stack이 비워진다면 event loop를 통해 task queue와 job queue들의 함수를 다시 call stack에 얹고 계쏙 반복....
  • 태스크 큐는 들어온 순서대로 콜백 함수를 내보낸다.
    이벤트 루프는 콜 스택이 비워졌을 때, 태스크 큐에서 콜백 함수가 들어온 순서대로 함수를 내보낸다. 단, 큐는 여러 개가 존재하며 그 중 우선순위가 높은 큐(잡 큐 등)에서 나중에 들어온 콜백 함수가 실행될 수 있다.
  • 비동기 코드를 처리하는 모듈은 JS engine 외부에 있다.
  • event loop, task queue, job queue등으로 구성된다.
  • API 모듈은 비동기 요청을 처리 후 태스크 큐에 콜백 함수를 넣는다.
  • JS engine은 call stack이 비워지면 task queue의 콜백함수를 실행한다.
request("user-data", (userData) => {
	console.log("userData 로드")
    saveUsers(userData)
});

console.log("DOM 변경")
console.log("유저 입력")
  • request()는 콜백함수를 받고 비동기적으로 작동된다. 그래서 비동기 처리가 끝났을 때 이 콜백함수를 task queue에 넣는다.
profile
智(지)! 德(덕)! 體(체)!

0개의 댓글