자바스크립트로 알아보는 동기/비동기, 블록킹/논블록킹

정의

각각의 정의를 핵심만 요약하면 다음과 같다.

  1. Block - 함수를 호출했을 함수의 모든 행위가 끝난 후에 제어권을 넘겨준다.
  2. Non block - 함수를 호출하면 함수의 종료와 상관없이 바로 제어권을 넘겨준다.
  3. Sync - 작업을 순차적으로 처리한다.
  4. Async - 작업을 순차적으로 처리하지 않는다.

Example Code

개념만 읽어서는 정확히 이해하기가 어렵다. 각각의 방식을 조합한 4가지방식에 대해 코드로 알아보자

  1. Blocking + Sync : 제어권을 끝날 때까지 안넘겨줌 + 순차적으로 실행
1
2
3
4
5
const sum1 = n => {
let sum = 0;
for(let i=1; i<=n; i++) sum += i;
return sum;
};

위 코드는 숫자 n을 받아 합계를 구하는 간단한 함수다. 이 함수 블록킹이자 동기 함수이다.
확인하기 위해서 다음을 실행해보자.

1
2
console.log(sum1(1e9));
console.log('B');

큰숫자를 인자로 넣어서 실행해보면 sum1 함수가 끝날 때까지 콘솔이 ‘B’가 찍히지 않고 완료되고나서 찍힌다.
끝날 떄까지 제어권이 아래로 넘어가지 않기 때문에 블록킹함수인 것을 확인할 수 있다.
또한 순차적으로 실행되기 때문에 당연히 동기방식이다.

  1. Blocking + Async : 순차적으로 실행할 필요가 없지만 + 제어권이 안넘어와서 결국 순차적으로 실행됨
1
2
3
4
5
6
7
8
const sum2 = (n, fn) => {
let sum = 0;
for(let i=1; i<=n; i++) sum += i;
fn(sum);
};

sum2(1e9, console.log);
console.log('B');

자바스크립트에서 비동기 함수를 만드는 방법중 하나는 callback 함수를 이용하는 것이다.
A행위와 B행위에 인과관계가 있을 때 순서에 상관없이 실행할 수 있는 방법은 B가 A와 인과관계가 있는 순차적인 동작을 A에게 위임(callback)하는 것이다.
B는 그래서 A와 상관없이 동작할 수 있지만, A함수 자체가 블록킹함수라서 결국에는 막혀 순차적으로 실행되게 된다. 결국 비동기의 의미가 없어진다 ㅠㅠ

무슨 의미가 있니 Blocking + Async는 안티패턴이다

  1. Non Blocking + Sync : 제어권이 바로 넘어가지만 + 순차적으로 실행하기 위해 계속 모니터링함

그렇다면 자바스크립트에서 논블로킹 함수는 어떻게 만들 수 있을까?
여러가지 방법이 있지만 가장 간단한 방법은 Web API에서 제공하는 setTimeout 함수를 사용하는 것이다.
Web API 같은 경우에는 Main UI 스레드와 다른 스레드에서 실행되기 때문에 제어권을 바로 넘겨줄 수 있다.

1
2
setTimeout(() => console.log('A'), 1000);
console.log('B');

실제로 위 코드를 실행하면 ‘B’가 먼저 출력되는걸 볼 수 있다. 바로 제어권이 넘어오므로 논블록킹 함수이다.
setTimeout을 이용하여 sum1 함수를 논블로킹 + 동기 함수로 만들면 다음과 같다.

1
2
3
4
5
6
7
8
9
10
const sum3 = n => {
const result = {isComplete: false};
setTimeout(() => {
let sum = 0;
for(let i=1; i<=n; i++) sum += i;
result.isComplete = true;
result.value = sum;
}, 0);
return result;
};

함수가 실행되고 결과값을 바로 리턴한다. 일정시간이 지나고 result.isComplete 이 true가 됐을 때
연산값이 result에 입력될 것이다. 자바의 Future와 비슷하다 ^^
위 함수를 실행하여 콘솔에 찍어보자

1
2
let future = sum3(1e9);
console.log(future.value);

실제 연산이 끝나기 전에는 undefined가 출력된다. 인과관계가 있어서 순차적으로 실행해야 한다면(동기) 다음과 같이 계속 작업이 끝났는지 모니터링 해줘야한다.

1
2
3
4
5
6
const id = setInterval(() => {
if(future.isComplete) {
clearInterval(id);
console.log(future.value / 100);
}
}, 10);

그만 물어봐계속 물어봐야됨 ㅎㅎㅎ

  1. Non Blocking + Async : 제어권을 바로 넘겨줌 + 콜백 함수를 주어서 자기일만 하면되므로 모니터링이 필요없다
1
2
3
4
5
6
7
8
9
10
const sum4 = (n, fn) => {
setTimeout(() => {
let sum = 0;
for(let i=1; i<=n; i++) sum += i;
fn(sum);
}, 0);
};

sum4(1e9, sum => console.log(sum / 100));
console.log('B');

실행해보면 ‘B’가 먼저 출력되는걸 볼 수 있다.

Quiz

이쯤에서 퀴즈~ 프론트웹에서 자주쓰는 jquery Ajax 함수는 어떤 방식으로 동작할까요?

1
2
3
4
5
const a = $.ajax("url").done(function(data) {
alert("success");
});

b();

정답은 …
제어권이 바로 b로 넘어가고, b와 순서상관없이 콜백(done)으로 처리하므로 비동기+논블락 입니다. ㅎㅎ

요약

조합표