반복적인 비동기 속도 개선하기

in #javascript7 years ago (edited)

자바스크립트는 비동기IO(입출력) 모델을 이용해서 여러개의 이벤트를 처리할 수 있습니다.
비동기IO 모델은 사용자가 이벤트가 발생했을때 기다리게 하지 않고 이벤트가 끝났을 때 알려줄 수 있는 경로만 제공해주는 것입니다. 그래서 이벤트가 발생 후 다른 이벤트를 또 발생시킬 수 있습니다.

내부에서는 여러개의 쓰레드와 프로세스가 병렬적으로 일을 처리하며, 내부에 있는 프로세스들이 일이 끝나면 이벤트가 발생했을때 알려준 경로를 통해 입출력이 끝났음을 알려줍니다.

hItkz.png

기존에는 비동기 이벤트가 많지 않았기 때문에 콜백이라는 함수를 이용해서 이벤트가 끝난것에 대해 알림을 받을 수 있었는데 자바스크립트가 발전하면서 비동기 이벤트가 많아지다 보니까 콜백으로 인해 소스코드가 보기가 불편해지는 상황이 생겨서 Promise라는 개념이 나오게 됬습니다.

//1번 함수가 끝나면 2번함수가 시작되고 2번함수가 끝나면 3번함수가 시작되는 경우 이런모습을 콜백헬이라고 부릅니다.
func1(function(){
    func2(function(){
       func3(function(){
      });
   })
});

비동기 함수가 n개인 경우 안쪽으로 n만큼 들어가게 되지만, Promise를 사용하게되면 콜백헬을 해결할 수 있습니다.

func1()
.then(func2)
.then(func3)
.then(function(){
})

만약 func1 func2 func3이 각각 3초간의 실행 시간을 가진다고 하면 위에 실행결과는 총 9초가 됩니다.

연관성이 없는 비동기 상황

항상 비동기 이벤트간 연관관계가 존재하는 것만은 아닙니다. 여러개의 디렉토리에서 파일을 동시에 삭제 시켜야되는 경우 1번이 먼저 삭제가 되든 2번이 먼저 삭제가 되든 상관이 없습니다.

//잘못된 비동기 사용
const fs = require('fs');

async function deleteDir(dir){
  var files = fs.readDirSync(dir);

  for(var i = 0; i< files.length;i++){
     await deleteFile(files[i]); // 폴더에 있는 파일을 하나씩 지운다. 
  }
}

deleteDir('test').then(_=>{ //async함수의 반환 결과값은 Promise객체 then으로 이벤트가 종료 되었음을 알 수 있다.
  console.log('삭제완료');
});

async와 await는 Promise를 좀 더 쉽게 사용할 수 있게 나온 문법입니다.
async함수는 Promise객체를 반환하고 안에서 실행되는 또 다른 Promise를 then을 쓰지 않고await로 기다려 비동기함수를 동기 함수 처럼 보이게 도와줍니다.

하지만 위에서 파일을 지우는 비동기 함수인 deleteFile가 실행되는데 5초가 걸린다면 파일의 개수 * 5초가 걸리게 됩니다.

그래서 저런 반복적인 비동기 상황을 처리하기 위해 Promise.all을 사용하시면 됩니다.

//잘못된 비동기 사용
const fs = require('fs');

async function deleteDir(dir){
  var files = fs.readDirSync(dir);
  return Promise.all(files.map(deleteFile));
}

deleteDir('test').then(_=>{ //async함수의 반환 결과값은 Promise객체 then으로 이벤트가 종료 되었음을 알 수 있다.
  console.log('삭제완료');
});

소스코드도 짧아졌고 이제는 실행속도가 파일을 1개 지우는 속도와 100개를 지우는 속도와 몇초도 차이가 나지 않습니다.

deleteFile함수가 Promise객체를 반환해준다고 가정하면 files.map(deleteFile)은 Promise array를 반환해줍니다.
Promise array는 Promise.all로 실행을 시킬 수 있고 Promise.all은 Promise array를 병렬로 처리를 한다음에 모든
Promise객체들이 종료가 되면 Promise객체를 반환해주는데 해당 객체를 리턴하기 때문에 deleteDir('test').then같은 문법으로 모든 파일을 지우고 나서 알림을 받을 수 있습니다.

연관관계가 있는 비동기 상황과 연관관계가 없는 비동기 상황을 고려한다면 프로그램의 실행속도를 더 빠르게 만들 수 있습니다.