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의 순서로 출력된다
비동기 작업을 처리하는 방법에는 콜백과 프로미스가 있다.
다음 아래의 두 예시로 어떤 차이점이 있는지 알아보자.
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로 적용하면 다음과 같다.
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의 state이란, promise의 처리 과정을 의미한다.
new Promise()로 프로미스를 생성하고 종료될 때까지 총 3가지의 상태를 갖는다.
어떠한 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을 할 수 없을 것이다.
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') };
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 |
댓글 영역