해나아부지 개발일지

자바스크립트 비동기처리(asynchronous javascript) 본문

Developers/JavaScript

자바스크립트 비동기처리(asynchronous javascript)

__APPA 2020. 8. 12. 00:19

싱글 스레드 환경인 자바스크립트 런타임을 멀티 스레드처럼 바꿔주는

자바스크립트의 비동기처리에 대해 알아보자.

 

싱글 스레드란?

일반적인 큐(Queue)로 예를 들어보면 큐에 일련의 작업들이 들어왔을 때 작업들은 들어온 순서대로 처리가 될 것이다.

1번과 2번이 동시에 처리가 된다거나 1번이 작업을 하다가 멈추고 2번이 시작을 하지 않는다는 이야기다. 우리가 프린트로 한글 문서들을

출력할 때 인쇄버튼을 누른 순서대로 출력이 될 것이다. 첫 번째 문서가 다 출력이 되야지만 두 번째 문서의 출력을 시작할 수 있다.

싱글 스레드에서는 현재 실행하고 있는 작업이 끝나기 전까지는 아무것도 할 수가 없다.

 

너튜브나 넷플릭스에서 동영상의 로딩이 동기적으로 실행된다면 우리는 로딩이 끝날때까지 아무것도 할 수 없을 것이다. 하지만 동영상로딩은 비동기적으로 처리되기 때문에 동영상이 로딩될 때 우리는 음량도 조절할 수 있고 메뉴에 들어가서 자막설정도 바꿀 수 있다. 

 

자바스크립트의 비동기처리

우리가 사용하는 웹 브라우저는 1초에 한번씩 리렌더링(Re-rendering)된다. 함수들이 실행될 때 콜스택(Callstack)에 실행되는 함수들이 쌓이고 콜스택이 비워질때까지 웹 브라우저의 렌더링은 중단된다. 앞서 설명했던 것 같이 함수들이 동기적으로 작동한다면 콜스택이 다 비워질때까지는 웹브라우저의 컨트롤이 불가능하다. 


비동기처리의 변천사

Async callback → Promise → Async&Await

 

Async callbacks

함수의 인수로 지정되며 백그라운드에서 코드 실행이 끝나면 callback 함수를 호출하여 작업이 완료됐음을 알리거나, 다음 작업을 실행하게 할 수 있습니다. callback을 사용하는 것은 구식이지만, 종종 사용이 되는 모습을 확인할 수 있다.

 

콜백(callback)은 다른 코드의 인수로서 넘겨주는 실행 가능한 코드를 말한다. 콜백을 넘겨받는 코드는 이 콜백을 필요에 따라 즉시 실행할 수도 있고, 아니면 나중에 실행할 수도 있다.

 

addEventListener의 두 번째 인수(익명함수)로 지정된 것이 callback의 한 종류다. 실행 가능한 코드로서 바로 실행되지는 않지만 btn이라는 버튼을 클릭했을 때 실행되는 함수로 기능을 하고 있다.

btn.addEventListener('click', () => {
  alert('You clicked me!');
});

 

비동기 처리를 다른 한 종류로 setTimeout이 있다. setTimeout은 Web API이고 함수 실행을 지연시켜주는 역할을 한다. 아래 코드를 실행시켜보면 '1'이 먼저 출력되고 3초 뒤에 '2'가 출력되고 마지막으로 '3'이 출력될 것 같다. 

console.log('1');

setTimeout(function() {
	console.log('2');
}, 3000);

console.log('3');

결론부터 말하자면 출력순서는 '1' -> '3' -> '2'이다. 위 코드의 실제 실행은 먼저 콜스택에 console.log('1')이 들어가고 실행되어 '1'이 출력된 이후에 setTimeout도 콜스택에 들어가서 실행이 되지만 setTimeout의 첫번째 인수인 callback은 비동기적으로 대기 상태가 된고 콜스택을 빠져나온다. 마지막으로 console.log('3')이 콜스택에 들어가고 '3'이 출력된 이후에 대기상태였던 callback의 timer(3000)가 시작되고 3초 뒤에 '2' 출력되는 것을 볼 수 있다.

 

setTimeout의 두 번째 인수인 timer를 0으로 바꿔보고 실행해보기를 추천한다. 비동기적으로 대기상태가 되는 것을 이해할 수 있다.

 

하지만 모든 callback이 비동기적으로 처리되는 것은 아니다. Array의 method인 forEach는 callback을 인수로 받지만 즉시 실행된다.

const nums = ['1', '2', '3', '4'];

nums.forEach((i) => {
  console.log(i);
});

//output
// 1
// 2
// 3
// 4

 

콜백 지옥(Callback Hell)

재귀(Recursion)와 비슷한 형태를 콜백지옥은 비동기적인 프로그래밍을 위해 함수의 인수로 지정한 callback내에 또 다른 callback이 함수의 인수로 들어오고 이러한 패턴이 계속 반복되어 끊임없는 들여쓰기와 난독화 된 코드를 볼 수 있게 된다. 

function doSomething(x, callback){
    callback(x)
}

doSomething(1, function(x1){
    doSomething(x1, function(x2){
        doSomething(x2, function(x3){
            doSomething(x3, function(x4){
                console.log("callback suck")
            })
        })
    })
})

이러한 문제를 해결하기 위해 나온 것이 Promise이다.


Promise

promise는 Object 객체 클래스의 상속을 받는 객체이다. new 생성자를 통해 callback을 인수로 받는 instance를 만든다.

만들어진 instance를 활용하여 비동기처리가 가능해진다. 

 

Promise 객체

 

Promise는 다음 중 하나의 상태를 가집니다.

  • 대기(pending): 이행하거나 거부되지 않은 초기 상태.
  • 이행(fulfilled): 연산이 성공적으로 완료됨.
  • 거부(rejected): 연산이 실패함.

Promise의 상태

비동기 처리 이후에 이행(fufilled) 됐을 때 실행할 함수(resolve)와 거부(rejected) 됐을 때 실행할 함수(reject)를 인수로 

지정해줘야 하며 실행 코드 또한 작성해줘야 한다.

 

// base form
const myPromise = new Promise(function(resolve, reject) {
   if(true) {
     resolve()
   } else {
     reject()
     }
   })
     
// return in function

const getPromise = (data) => { //
  return new Promise((rs, rj) => { // 인수의 이름은 정하기 나름이다(구분만 되면 된다)
    rs(data) // 프로미스가 이행상태일 때 ( )의 값 자체를 return해준다
  )
}

 

 

 

프로미스 체이닝(Chaining)

프로미스는 한번에 하나의 작업만 할 수 있다. 그래서 여러가지 작업들을 비동기적으로 차례대로 처리하기 위해서는 

'then'을 사용하여야 한다. 

let myFirstPromise = new Promise((resolve, reject) => {  
  
  setTimeout(function(){
    resolve("Success!"); 
  }, 1000);
});

myFirstPromise.then((successMessage) => {
  console.log("Yay! " + successMessage);
});

비동기적으로 작업이 성공하면 resolve(...)가 실행될 것이고 실패하면 reject(...)가 실행될 것이다. 

프로미스의 인스턴스를 생성하면 'then'이라는 메서드를 사용할 수 있고 callback을 인수로 받는다. 

각 단계가 성공하면 return값이 다음 단계로 전달된다. 

위 예제를 보면 myFirstPromise의 실행결과가 성공하면 resolve의 "Success!"가 then의 successMessage라는 인수에 

전달이 되고 console에 "Yay! Success!"가 출력되는 것을 볼 수 있다.

 

 

콜백지옥을 해결하기 위해 만들어진 프로미스지만 프로미스 내부에 프로미스를 사용하는 형태로 잘못 사용하면 프로미스지옥에 빠질 수 있다. then을 이용해 체이닝을 잘하면 된다!


 

Comments