aync/await를 map안에서 이용하려면?

마데슾 : My Dev Space·2020년 3월 22일
2

비동기처리

목록 보기
2/3
  • async/await
    - "비동기로 돌아가는 코드를 동기적으로" 작동하게끔 하는 기능을 한다.
    - async 함수 내에서 await 키워드를 작동시키면 "원래는 비동기적으로 일어나야 하는 일이 기다려진다" 라고 생각할 수 있다.

  • Promise.all

    • 여러개의 비동기 함수가 한번에 실행되는 경우에 처리를 돕기 위해서 만들어진 함수이다.
    • 여러개가 실행되어도, 모든 함수가 실행될 때까지 기다리고 순서대로 결과를 넣어서 보여준다.
const fs = require("fs");
const util = require("util");

const {
  getBodyFromGetRequestPromise,
  getDataFromFilePromise
} = require("../exercises/02_promiseConstructor");

const writeFilePromise = util.promisify(fs.writeFile);

const BASE_URL = "https://koreanjson.com/users/";

const fetchUsersAndWriteToFileAsync = async (readFilePath, writeFilePath) => {
  const dataList = await getDataFromFilePromise(readFilePath); // [1,2,3,4,5]

  let responseDataList;

  const responseDataListFunc = async () => {
    return await dataList.map(async reqData => {
      return await getBodyFromGetRequestPromise(BASE_URL + id);
      // 여기서 await getBodyFromGetRequestPromise(BASE_URL + id)를 콘솔에찍으면 결과값이 잘나온다
    });
  }; 

  responseDataList = responseDataListFunc();

  console.log("responseDataList ? ", responseDataList);
};

module.exports = {
  fetchUsersAndWriteToFileAsync
};

문제가 있었던 코드는 아래의 코드..!

const fetchUsersAndWriteToFileAsync = async (readFilePath, writeFilePath) => {
  const dataList = await getDataFromFilePromise(readFilePath); // [1,2,3,4,5]

  let responseDataList;

  const responseDataListFunc = async () => {
    return await dataList.map(async reqData => {
      return await getBodyFromGetRequestPromise(BASE_URL + id);
    });
  }; 

  responseDataList = responseDataListFunc();

  console.log("responseDataList ? ", responseDataList);
};

map 메소드를 통해 각각의 url에서 응답받은 데이터로 dataList를 매핑하려했지만.. 콘솔에 찍힌 결과값은... 아래와 같다.

const responseDataListFunc = async () => {
    let result = await dataList.map(async id => {
      return await getBodyFromGetRequestPromise(BASE_URL + id);
    });

    console.log("result ? ", result);
  };

이렇게 map의 결과값을 result에 담아 콘솔에 찍어보면 아래와같이 promise 대기 상태가 찍힌다

const responseDataListFunc = async () => {
  await dataList.map(async id => {
    let result = await getBodyFromGetRequestPromise(BASE_URL + id);
    console.log("result ? ", result);
  });
};

위와같이 콘솔에 찍어보았을땐 각각의 url에서 응답받은 데이터가 콘솔에 잘 찍힌다. 왜 map 메소드 실행 결과는 Promise { <pending> }이 될까? 구글링을 해보았다.

await aync map라고 검색해보니 지금 내가 고민하고있는 문제를 다루는 글이 있었다..!!

await는 Promise 객체를 실행하고 기다려주지만, Promise 배열로는 그렇게 할 수 없기 때문이라는 사실을 알게되었다. 지금 위의 코드는 arr.map을 통해 Promise 배열을 리턴하게 구현했기때문에 await가 의미가 없다.

프로미스 배열을 처리하는 방법


상황에 따라 위의 2가지 방법중 하나를 선택한다.

  • Promise.all
    • 배열 내의 모든 Promise 가 통과될 때까지 기다리는 Promise 를 반환한다
    • 배열 내의 모든 Promise 를 중간에 거부(reject or Exception)가 발생하더라도 다 실행한다
  • Promise.race
    • 도중에 하나라도 실패하면 즉시 리턴되는 Promise 를 반환한다.
    • 거부 발생시 중도에 중단된다
const fetchUsersAndWriteToFileAsync = async (readFilePath, writeFilePath) => {
  const dataList = await getDataFromFilePromise(readFilePath); // [1,2,3,4,5]

  const responseDataList = await Promise.all(
    dataList.map(id => {
      return getBodyFromGetRequestPromise(BASE_URL + id);
    })
  );

  console.log("responseDataList ? ", responseDataList);
};

위와같이 코드를 수정하니 원하던 결과값을 얻을 수 있었다.

추가)))

for ...of를 사용해 해결하는 방법도 있다

async function processArray(array) {
  for (const item of array) {
    await delayedLog(item);
  }
  console.log('Done!');
}

// output
1
2
3
Done!

참고 블로그

map, reduce 함수에서 async/await 쓰기
async/await를 이용한 비동기 loop 병렬로 순차 처리하기
Promise
자바스크립트 Promise 쉽게 이해하기

profile
👩🏻‍💻 🚀

1개의 댓글

comment-user-thumbnail
2021년 9월 2일

도움이 많이 됐습니다! 고민하던 부분이 한 번에 풀렸네요😆

답글 달기