상세 컨텐츠

본문 제목

Promise, Asynchronous, async & await - Javascript

Programming/Concept

by 쌩우 2019. 6. 21. 20:34

본문

Javascript는 싱글 스레드 언어이기 때문에 비동기적인 작업의 처리가 효율성의 관건으로 생각된다.

아래의 코드를 보며 비동기적인 작업이 어떤 것인지 간략히 살펴보자.
아래에서 비동기를 일으키는 주요 원인은 "setTimeout" 함수이다.

const printString = (string, callback) => {
    setTimeout(
      () => {
        console.log(string)
        callback()
      }, 
      Math.floor(Math.random() \* 100) + 1
    )
  }
  const printAll = () => {
    printString("A", () => {
      printString("B", () => {
        printString("C", () => {})
      })
    })
  }
  printAll()
  //시간이 아무리 랜덤하게 지정되더라도 항상 A, B, C의 순서로 출력된다

비동기 작업을 처리하는 방법에는 콜백과 프로미스가 있다.
다음 아래의 두 예시로 어떤 차이점이 있는지 알아보자.

callback

const getDataFromFile = function(filePath, callback) {
  fs.readFile(filePath, 'utf8', (err, data) => {
    if(err) {
        callback(err, null)
      } else {
        let array = data.toString().split("\\r\\n")
        callback(err, array)
      }
    })
};

const getBodyFromGetRequest = function(url, callback) {
  request(url, function(err, response, body) {
      if(err) {
        callback(err, null)
      } else {
        callback(err, JSON.parse(body))
      }      
    })
 };

promise

위의 코드를 promise로 적용하면 다음과 같다.

const getDataFromFilePromise = filePath => {
  return new Promise(function(resolve, reject) {
    fs.readFile(filePath, 'utf8', (err, data) => {
      if(err) {
        reject(err)
      } else {
        let array = data.toString().split("\\r\\n");
        resolve(array)
      }
    });
  });
};

const getBodyFromGetRequestPromise = url => {
  return new Promise((resolve, reject) => {
    request(url, (err, response, body) => {
      if(err) {
        reject(err)
      } else {
        resolve(JSON.parse(body))
      }
    })
  })
};

Promise의 states

Promise의 state이란, promise의 처리 과정을 의미한다.
new Promise()로 프로미스를 생성하고 종료될 때까지 총 3가지의 상태를 갖는다.

  1. Pending(대기) : 비동기 처리 로직이 아직 미완료인 상태
  2. Fulfilled(이행) : 비동기 처리가 완료되어 프로미스가 결과값을 반환한 상태
  3. Rejected(거절) : 비동기 처리가 실패했거나 오류가 발생한 상태

Promise.all & Promise chaining

어떠한 promise들을 비동기적이 아닌, 모두 resolve 된 이후에 어떤 작업을 덧붙이고 싶을 때 사용해준다.

const fs = require("fs");
const util = require("util");

const {
  getBodyFromGetRequestPromise,                        //get 방식으로 request하는 promise
  getDataFromFilePromise                            //request에 쓸 endpoint의 문자열이 담긴 파일을 readFile하여 data를 꺼내오는 promise
} = require("../exercises/promiseConstructor");

const writeFilePromise = util.promisify(fs.writeFile);            //get으로 받은 data로 writeFile 하는 promise

const BASE\_URL = "https://koreanjson.com/users/";

const fetchUsersAndWriteToFile = (readFilePath, writeFilePath) => {
  return getDataFromFilePromise(readFilePath)                                //먼저 file을 읽고, end point의 url 가져온다
    .then((userlist) => {return userlist.map(userid => BASE\_URL + userid)})        //가져온 url을 get 요청할 url 뒤마다 붙여준다
    .then((urlList) => {return urlList.map(url => getBodyFromGetRequestPromise(url))})    //각각의 url에 따라 모두 get 요청하는 프로스  생성 
    .then(requests => {return Promise.all(requests)                                    //요청했던 정보가 모두 돌아오기를 기다려야 하므로, Promise.all 안에 요청 프로미스 모두를 담는다
          .then(res => {
              let names = res.map(infos => infos.name).join('\\n')                //응답받은 정보들을 토대로, name 값만 추출
              return writeFilePromise(writeFilePath, names + '\\n', 'utf8')    //추출한 이름들로 writeFile 실행하는 프로미스 return
          })
          .catch(err => console.log(err))
      })
  }

GET 요청에 대한 응답을 동기적으로 만들어줘서, 그 다음에 writeFile을 실행하는 부분이 포인트였다.
Promise.all과 같은 방법으로 해당 요청 프로미스의 pending을 resolve까지 풀어주지 않으면,
원하는 시점에서 정보를 받아 writeFile을 할 수 없을 것이다.

async & await

const fetchUsersAndWriteToFileAsync = async (readFilePath, writeFilePath) => {
  const readFile = await getDataFromFilePromise(readFilePath);
  const urls = readFile.map(userid => BASE\_URL + userid)
  const requests = urls.map(url => getBodyFromGetRequestPromise(url))
  const response = await Promise.all(requests)                //위의 코드와 마찬가지로, request에 대한 프로미스의 pending을 모두 풀어준 뒤에, writeFile에 대한 프로미스를 예상했던 흐름으로 작성할 수 있게 된다.
  const names = response.map(infos => infos.name).join('\\n')    
  return writeFilePromise(writeFilePath, names + '\\n', 'utf8')
};

'Programming > Concept' 카테고리의 다른 글

Authentication 목표  (0) 2019.06.26
Database sprint  (0) 2019.06.24
Server Side Technics - Solo Sprint  (0) 2019.06.21
express, fs, body-parser - nodejs  (0) 2019.06.20
checkpoints 12 - module exports, setTimeOut, event loop  (0) 2019.06.19

관련글 더보기

댓글 영역