Asynchronous(2)

안정태·2021년 4월 26일
0

Study

목록 보기
11/33

비동기의 이론적인 부분은 얼추 이해를 했다. 하지만 완벽하게 나에게 설명을 하라고 한다면 할 수 없을 것 같다. 그 만큼 아직 완벽하게 나 자신이 이해하지 못하고 있다는 것이다. 하지만 그렇다고 텍스트만 쳐다본다고 갑자기 깨닫게 되는 건 아닌 것 같다.

그래서 일단 부딪혀보자는 생각으로 문제를 풀어보았다. 아직 이게 완벽한 정답인지 확실히는 알 수 없지만 일단 내가 푼 방식을 한번 얘기해 보려 한다.

callBack 구현

const getDataFromFile = function (filePath, callback) {
  // TODO: fs.readFile을 이용해 작성합니다
};

getDataFromFile('README.md', (err, data) => console.log(data))

먼저, 콜백 함수를 이용해서 다음 비동기 처리를 구현하는 것이 문제다. fs.readFile함수를 이용해서 구현해 내면 된다.
fs.readFile함수의 개략적인 사용 방법은 다음과 같다.
fs.readFile(파일[, 인코딩 방식 || 플레그], 콜백함수) - 기본형

const getDataFromFile = function (filePath, callback) {
  // TODO: fs.readFile을 이용해 작성합니다
  fs.readFile(filePath,'utf8', function(err, data){
    if(err) {
      callback(err, null)
    }else{
      callback(null, data)
    }
  });
};

getDataFromFile('README.md', (err, data) => console.log(data))

위 와 같이 fs.readFile함수가 err 를 받았을 때 콜백 함수에 전달되는 인자값을 나눠주고 data 를 받았을 때 콜백 함수에 전달되는 방식을 나눠 주었다. 앞서 말했듯이 이 콜백함수를 이용한 구현은 매 데이터 처리 시마다 err 를 검사해야한다는 비효율이 발생한다.

Promise 구현

const getDataFromFilePromise = filePath => {
  return new Promise((resolve, reject) => {
    fs.readFile(filePath,'utf8', function(err, data){
      if(err) {
        reject(err)
      }else{
        resolve(data)
      }
    })
  })
  // TODO: Promise 및 fs.readFile을 이용해 작성합니다.
};

getDataFromFilePromise('README.md').then(data => console.log(data));

위 함수는 콜백함수로 만들어준 함수와 같은 기능을 하고있다. 단지, 차이점은 이 함수는 Promise 를 이용해서 구현했다는 것이다. fs.readFile함수가 err를 전달한다면 reject를 받아서 오류의 원인을 제공할 것이다. 하지만 정상적으로 data를 전달한다면 resolve에 의해서 정상적으로 data.then이 받아서 출력해준다.

Chaining 사용

여기까지는 어느정도 감이 잡혔을 것이다. 그렇다면 이렇게 구현된 함수들을 어떻게 활용할 수 있을까?

const readAllUsersChaining = () => {
  // TODO: 여러개의 Promise를 then으로 연결하여 작성합니다
  let array = [];
  return getDataFromFilePromise(user1Path).then(function(userData){
    array.push(JSON.parse(userData))
    return getDataFromFilePromise(user2Path);
  }).then(function(userData){
    array.push(JSON.parse(userData))
    return array
  })
}

readAllUsersChaining();

위 코드는 뭔가 더 복잡해 보일 수도 있지만 단지, 함수의 이름이 길어서 그렇게 보일 뿐이다. 이는 위에서 만들어둔 함수를 그대로 이용하고 user1Path,user2Path라는 문자열로 이루어진 객체를 가지고 있다.
그렇다면 여기서 getDataFromFilePromise함수는 무엇을 리턴할까? 위 함수를 보면 알 수 있듯
new Promise()객체를 리턴하고 있다.
그렇다면 이 객체에는 바로 .then 메소드를 이용할 수 있다. 때문에 .then안에 있는 함수에는 user1Path의 값이 전달되고 그 값을 가지고 함수가 실행된다.

function(userData){
  array.push(JSON.parse(userData))
  return getDataFromFilePromise(user2Path);
}

위 함수를 실행함으로써 user1Path값을 JSON.parse() 해준 결과를 배열에 넣어준다. 그리고 다시 getDataFromFilePromise(user2Path) 를 통해 새로운 Promise객체를 리턴해줘서 바로 다시 .then 을 사용할 수 있게 해준다. 이때 user2Path를 받을 함수는 다음과 같다.

function(userData){
  array.push(JSON.parse(userData))
  return array
}

마찬가지로 받은 데이터를 JSON.parse() 를 통해 배열에 넣어주고 최종적으로 완성된 배열을 리턴해주는 것이다.

PromiseAll 사용

Promise.all()메서드는 순회 가능한 객체에 주어진 모든 프로미스가 이행한 후, 혹은 프로미스가 주어지지 않은 경우 이행하는 프로미스를 반환 합니다. 즉, 여러 프로미스의 결과를 집계할 때 유용하게 사용 가능한 메서드이다.

const readAllUsers = () => {
  // TODO: Promise.all을 이용해 작성합니다
  var p1 = getDataFromFilePromise(user1Path)
  var p2 = getDataFromFilePromise(user2Path)
  return Promise.all([p1, p2]).then(([data1, data2]) => {
    return JSON.parse(`[${data1},${data2}]`)
  })
}

readAllUsers()

위 와 같이 프로미스 객체를 변수로 선언해주고 그 프로미스를 Promise.all의 인자로 주고 그 인자에 대해서 함수를 실행해주는 것이다.

async & await

const readAllUsersAsyncAwait = async () => {
  // TODO: async/await 키워드를 이용해 작성합니다
  let data1 = await getDataFromFilePromise(user1Path)
  let data2 = await getDataFromFilePromise(user2Path)
  return JSON.parse(`[${data1},${data2}]`)
}

readAllUsersAsyncAwait();

마찬가지로 async & await 문법을 사용해서 비동기함수를 동기적인 함수의 형태로 나타내 준 것이다.

profile
코딩하는 펭귄

0개의 댓글