본문 바로가기
두드리는 개발자 홍차/Nodejs

Nodejs 논블로킹 - 비동기, 콜백, Promise

by 홍차23 2019. 12. 11.

블로킹과 논블로킹 

  • 블로킹: Nodejs 프로세스에서 추가적인 자바스크립트 실행을 위해 자바스크립트가 아닌 작업이 완료될 때까지 기다려야 하는 상황이다. 
  • 논블로킹: 특정 로직의 실행이 끝날 때까지 기다리지 않고 나머지 코드를 먼저 실행하는 비동기 처리. 예를 들어 클라이언트에서 서버로 데이터를 요청했을 때 서버의 응답을 기다리며 다른 코드를 실행하지 않는다면 웹 애플리케이션이 실행되는 데 아주 오랜 시간이 걸리게 된다. 따라서 javascript 는 비동기 처리를 필요로 한다. 
    • 비동기의 문제점: 데이터를 요청하고 응답을 기다리지 않고 다음 코드를 실행하므로 결과가 undefined 로 나온다. 쉽게 말하면 "보노보노 너는 몇 살이니?"라고 물음을 던져놓고 대답이 돌아오기 전에 "아 그렇구나(초기값없음=undefined)" 자문자답해버리는 것. 그럼 수많은 웹 애플리케이션들이 어떻게 서버의 대답을 듣고 작동하는걸까? 답은 콜백함수다.

콜백함수

  • 콜백 함수: 어떤 이벤트가 발생한 후에 수행될 함수. 비동기 처리 방식의 문제점을 해결. 데이터가 준비된 시점에 작동.
    • 콜백의 문제점: 콜백지옥. 해결책은 promise, async 또는 콜백함수 분리.
  • Nodejs 표준 라이브러리의 모든 I/O 메서드는 논블로킹 비동기 방식을 제공하고 콜백 함수를 받는다. 일부 메서드는 블로킹 메서드도 가지는데 이 경우 이름 마지막에 Sync 가 붙는다.
const fs = require('fs');
const data = fs.readFileSync('/file.md'); //파일을 읽을 때까지 여기서 블로킹

Nonblocking 

const fs = require('fs');
fs.readFile('/file.md', (err,data)=>{
	if (err) throw err;
});

 

프로미스

참고: https://joshua1988.github.io/web-development/javascript/promise-for-beginners/#promise%EA%B0%80-%EB%AD%94%EA%B0%80%EC%9A%94

  • Promise: 자바스크립트 비동기 처리에 사용하는 객체. 
  • ajax 통신 코드
function getData(callbackFunc) {
	$.get('url주소/products/1', function(response) {
    callbackFunc(response); // 서버에서 받은 데이터 response를 callbackFunc() 함수에 넘겨준다.
    });
}

getData(function(tableData) {
	console.log(tableData); //$.get()의 response 값이 tableData에 전달된다.
});

 

  • 프로미스 적용
function getData(callback) {
	return new Promise(function (resolve, reject) {
    $.get('url 주소/products/1', function (response) {
      resolve(response); //데이터를 받으면 호출
      });
    });
}

getData().then(function(tableData) { //getData() 실행이 끝나면 then() 호출
	console.log(tableData); //resolve() 결과값이 전달됨
});

promise 의 3가지 상태

  • pending
  • fullfilled
  • rejected

싱글스레드 기반의 JS 

자바의 경우 멀티스레딩을 통해 여러 가지 작업을 동시에 수행한다. 그에 반해, 자바스크립트는 싱글 스레드 기반이다. 즉, 단일 호출 스택을 가지고 있고, 한 번에 하나의 작업만 수행할 수 있다. 하지만 위에서 보았듯이 자바스크립트는 비동기적으로 먼저 실행된 코드의 작업이 끝나기 전에 더 나중에 실행된 코드 작업이 먼저 끝날 수도 있다. 어떻게 가능한 걸까.

function a(){
	b()
    console.log("a")
}

function b(){
	console.log("b")
}

a()

//result
b
a

 

setTimeout(function(){
	console.log("All task was done.");
}, 1000);

Web API, Task Queue, Event loop

참고: https://hudi.kr/%EB%B9%84%EB%8F%99%EA%B8%B0%EC%A0%81-javascript-%EC%8B%B1%EA%B8%80%EC%8A%A4%EB%A0%88%EB%93%9C-%EA%B8%B0%EB%B0%98-js%EC%9D%98-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC-%EB%B0%A9%EB%B2%95/

1. 코드가 실행되고 setTimeout 함수 실행된다. Call stack 에 setTimeout 함수가 추가된다.

2. setTimeout 함수는 Web API 가 처리한다. setTimeout 이 담긴 Callback 함수를 전달하고 동시에 setTimeout 작업을 요청한다. 

3. Call stack 에서 모든 작업이 완료되었으므로 setTimeout 작업이 제거된다.

4. Web API 는 setTimeout 작업을 실행한다. 1초를 기다린다.

5. 1초가 지나고, Task Queue로 Callback 함수 전달한다.

6. Event loop 는 항상 Call stack 이 비어있는지, Task queue에 작업이 있는지 검사한다. call stack 이 비었고, task queue에 작업이 추가되어 있음을 확인한다.

7. Task queue에서 대기하던 callback 함수를 call stack 으로 보낸다.

8. callback 함수 작업도 완료되어 pop 되고, 프로그램이 종료된다.

 

결론

Javascript 엔진은 주어진 코드를 실행한다. 

코드 실행의 스케줄링은 Javascript 엔진이 호스팅된 런타임 환경이 맡는다. 

 

 

다음 공부할 것>

 

-자바스크립트 async 와 await 

https://joshua1988.github.io/web-development/javascript/js-async-await/

-Nodejs http 트랜잭션

https://nodejs.org/ko/docs/guides/anatomy-of-an-http-transaction/

댓글