Programming/JavaScript

JS 동작 원리 / 비동기 동작

녱녱 2022. 11. 16.

JavaScript 동작

  • 자바 스크립트는 싱글스레드로 동작
    • 메인스레드 하나로 구성됨(하나의 호출 스택 사용) -> 한번에 하나의 작업만 수행할 수 있음
    • 작업이 차례대로 실행됨 -> 하나의 작업이 끝날 때 까지 또 다른 작업을 실행하지 않음
      • 호출 스택에 쌓인 함수/코드를 위에서부터 아래로 실행
      • 하나의 작업이 끝나면 바로 pop하고 아래의 코드 실행
    • 즉, JS는 런타임에서 자체적으로 비동기 API를 지원하지 않음!
    • 동시성을 보장하는 비동기, non-blocking 작업은 JS 엔진을 구동하는 런타임 환경(브라우저/Nodejs)에서 지원
      • 즉, JS는 코드를 그대로 실행만 하고 런타임 환경에서 이벤트를 스케줄 해주고 비동기 작업을 처리

JavaScript 런타임

  • JS는 런타임에서 메모리힙과 콜스택으로 구성됨
  • 런타임 : 특정 언어로 만든 프로그램들을 실행할 수 있는 환경(Node.js/브라우저)
    • 메모리힙 : 메모리 할당을 담당, 코드에 선언된 함수를 저장
    • 콜스택 : 코드를 호출하며 스택으로 쌓거나 Web API로 보냄, LIFO 방식(나중에 들어온 함수부터 처리)
    •  
      img
    • img
  • 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이 아닌 다른 환경에서 동작을 하게 하는 것
    1. 코드가 호출 스택에 쌓이고, 실행되면 , JS의 엔진은 비동기 작업을 Web API 에게 넘김
    2. Web API는 해당 작업을 수행, 콜백함수를 이벤트 루프를 통해 콜백 큐에게 넘겨줌
    3. 이벤트 루프는 콜스택에 쌓인 함수가 없을 때, 콜백 큐에 대기하던 콜백함수를 콜스택으로 넘겨줌
    4. 콜스택에 쌓인 콜백함수를 실행, 콜스택에서 제거함
  • 하지만! 비동기와 동기를 구분하는 기준과 block/non-block을 구분하는 기준은 다름!

비동기 동작

  • 동기 작업 : 작업들의 시작/종료 시점 및 순서를 결정할 수 있는 작업
  • 비동기작업 : 여러 작업들 중 일부 작업들의 시작 종료 시점을 알 수 없는 작업들을 비동기 작업이라 함

이벤트 루프

img

  1. call stack에서 비동기 callback 함수가 호출
  2. 해당되는 비동기 callback 함수가 Wep API(background)로 이동됨, background는 call stack이 실행되는 스레드와는 독립적으로 동작
  3. 비동기 작업이 마무리 되면 내부함수를 task queue에 전달
  4. taske queue에 전달된 내부 함수는 call stack 이 빌 때 까지 기다림
  5. 독립적으로 실행되는 call stack의 모든 함수가 종료(anonymous까지 종료되면)
  6. 이 때 task quere에 있던 내부함수를 call stack으로 올려 내부함수를 수행

callback 함수

  • 다른 함수의 파라미터로 이용되는 함수
  • 어떠한 이벤트에 의해 호출되는 함수
  • 코드를 통해 명시적으로 호출하는 것이 아님. 함수를 등록하기만 하고 어떤 이벤트가 발생하거나 특정 시점에 도달했을 때 시스템에서 호출하는 함수를 말함
  • 가독성을 떨어트리고 디버깅을 어렵게함

Promise

  • callback 함수의 단점을 극복하기 위해 등장
  • 비동기 메소드에서 마치 동기 메소드처럼 값을 반환 할 수 있음
    • 실제 값을 반환하지는 않고, 프로미스 객체를 반환해서 미래에 값이 반환되는 시점에 결과를 제공
  • Promise 객체 : 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다
    • 대기(pending) : 이행되거나 거부되지 않은 초기 상태
    • 이행(fulfilled) : 연산이 성공적으로 완료
    • 거부(rejected) : 연산 실패
      • then : 프로미스를 잘 이행 해 결과값을 받으면 처리해줄 수 있음(프로미스 인스턴스의 프로토타입)
      • catch : 실패한 이유를 받음(프로미스 인스턴스의 프로토타입)
      • finally : 프로미스가 처리된 후(이행/거부된 후) 실행되어 원하는 처리를 할 수 있음(프로미스 인스턴스의 프로토타입)
    •  
    • Flowchart showing how the Promise state transitions between pending, fulfilled, and rejected via then/catch handlers. A pending promise can become either fulfilled or rejected. If fulfilled, the "on fulfillment" handler, or first parameter of the then() method, is executed and carries out further asynchronous actions. If rejected, the error handler, either passed as the second parameter of the then() method or as the sole parameter of the catch() method, gets executed.
  • 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/@zlatkov

https://blog.sessionstack.com/how-javascript-works-event-loop-and-the-rise-of-async-programming-5-ways-to-better-coding-with-2f077c4438b5

https://medium.com/codex/javascript-event-loop-callback-queue-f15a4dd7f32b

https://donologue.tistory.com/382

https://velog.io/@curiosity806/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-%EB%B9%84%EB%8F%99%EA%B8%B0-%EB%8F%99%EC%9E%91

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

댓글