비동기 처리, 콜백함수
FE BE 개발 메모장/Javascript

비동기 처리, 콜백함수

비동기처리란?

특정 코드의 연산이 끝날 때 까지 코드의 실행을 멈추지 않고, 순차적으로 다음 코드를 먼저 실행하는 자바스크립트의 특성(싱글스레드, 콜스택). 즉, 요청을 보낸 후 응답에 관계없이 다음 동작을 실행한다.

 

동기와 비동기의 차이점

 

  • 동기(sync): 요청을 보낸 후 해당 응답을 받아야 다음 동작을 실행한다.
  • 비동기(async: 요청을 보낸 후 응답에 관계없이 다음 동작을 실행한다.

Sync와 Async차이

 

 

비동기 처리를 위해서 사용하는 메소드중 대표적인 setTimeout()이 있다. Web API의 한 종류로 코드를 바로 실행하지 않고, 지정한 시간만큼 기다렸다가 로직을 실행한다.

 

//5초후에 console.log를 띄움
setTimeout( () => {
   console.log('this is timer');
   }, 5000);

console.log('this is watch');

 

비동기 처리를 알기 전까진 출력의 순서는 다음과 같다고 생각했었다.

  • 5초뒤에 this is timer 출력
  • this is watch 출력

setTimeout()은 비동기 방식으로 실행되기 때문에 코드를 수행할 때, setTimeout()을 실행하고, 두번째 console.log('this is watch')를 실행한다. 그리고 5초뒤에 'this is timer'를 띄우게된다. 실제 순서는 다음과 같다.

 

  • this is watch 출력
  • 5초뒤에 this is timer 출력

callback함수로 비동기 처리

콜백함수는 파라미터(parameter)로 함수를 전달받아 함수 내부에서 실행하는 함수이다. 

const printString = (string, callback) => {
    setTimeout(
      () => {
       console.log(string)
       callback()  
    },
    Math.floor(Math.random() * 1000) + 1
    )
}

const printAll = () => {
  printString("A", () => {
    printString("B", () => {
      printString("C", () => {})
    })
  })
}
printAll()

 

위 코드를 해석하면 다음과 같다.

setTimeout() Web API를 통해 각각의 콜백함수는 n초의 시간을 갖게된다.

 

  printString("A", callback()     = 306ms
  printString("B", callback()     = 158ms
  printString("C", callback(){?}  = 91ms

 

 printAll()을 통해 순서대로 결과를 반환한다.

가장 먼저 콜백함수 printString*("A", () )가 실행되고, 순서대로 "B", "C"가 실행된다. 콜백함수의 제일 마지막 까지 도달했으면 다시 처음 콜백 함수부터 N초 후에 결과를 반환한다.(ex. n초 후에 A, n초 후에 B) 

 

 306ms 후 A
 158ms 후 B
 91ms  후 C

 

 

비동기 에러 처리

자바스크립트에서 콜백을 사용할 때, 예외처리가 어려워지기 때문에 오류 우선 콜백이라는 패턴이 생겼다.

아래의 코드는 비슷한 표현의 예제로 작성되었다.

 

const funcName = callback() => {
 
 //만약 상태가 이상하거나 에러난 경우
 if(에러가 났다!){
 	callback(에러발생!, null)
 }else {
    callback(null, 정상적으로 잘됨)
 }
}

<funcName>((err, data) => {
 if (err) {
    console.log("에러가 났다");  
  	return <>;   
  }
  return data
})

 

node.js를 이용한 방법

 

const fs = require('fs');
const path = require('path');

const firstDir = path.join(__dirname, 'directori')

let options = {
  encoding: 'utf8',
  flag: 'r'
}

fs.readFile(fileDir, options, (err, data) => {
    if(err){
	  callback(err, null);
    } else {
      callback(null, data);
    }

 

콜백 지옥(Callback hell)

비동기 프로그래밍에서 실행 순서를 신경쓰며 코딩을 해야한다. 아래와 같이 콜백지옥에 빠질 수 있기 때문이다.

step1(function (value1) {
    step2(function (value2) {
        step3(function (value3) {
            step4(function (value4) {
                step5(function (value5) {
                    step6(function (value6) {
                        // Do something with value6
                    });
                });
            });
        });
    });
});

 

대문자 알파벳을 출력하기위해 여러개의 콜백함수를 중첩해서 사용 하게 되는데, 요구하는 작업이 늘어날수록 더욱 더 복잡하고 가독성이 떨어지는 코드가 된다. 그래서 가독성을 높혀주기 위해 콜백함수를 지정해 주는 것이 좋다.

 

step1(afterStep1);
const afterStep1 = (value1) => {
    step2(afterStep2);
}

const afterStep2 = (value2) => {
    step3(afterStep3);
}

const afterStep2(value2) => {
    step3(afterStep3);
}

const afterStep2(value2) => {
    step3(afterStep3);
}

.
.
.
.

 

위와 같이 기명 함수로 인자를 넘기게 되면 콜백지옥은 어느정도 해결할 수 있다. 또 다른 콜백 지옥을 해결할 방식으로는 

Promise와 async & await 방식이 있다.

'FE BE 개발 메모장 > Javascript' 카테고리의 다른 글

async & await  (0) 2021.02.02
Promise  (0) 2021.02.01
자바스크립트 Prototype과 Instance  (0) 2021.01.15
객체(Object)와 객체를 생성하는 방법.  (0) 2021.01.14
함수 메소드(call, apply, bind)  (0) 2021.01.14