user346206
user346206

Reputation: 313

Axios forEach request, get response and add it to an array

I have a function which takes a array of GitHub repository names and then I want to send a request with every repository, to get the data about used languages in this repository and add it to an array. It looks like this:

function getRepositoryLanguages(owner, repos) {
  var stats = [];
  repos.forEach(async (repoName) => {
    try {
      repoLang = await axios.get(
        `https://api.github.com/repos/${owner}/${repoName}/languages`,
        {
          headers: {
            Authorization: "token ${token}",
          },
        }
      );
      stats.push(repoLang.data, repoName);
    } catch (error) {
      console.log(error);
    }
  });
  return stats;
}

I want to get language statistic for every reposository and then store it in array called stats. The thing is that this function return an empty array, when I am doing something like this:

getRepositoryNames(username).then((repos) => {
  console.log(getRepositoryLanguages(username, repos));
});

I just get an [].

Does anyone knows how to write this function properly with keeping in mind that those functions will be used in the app.get(/stats) route?

Upvotes: 1

Views: 97

Answers (2)

dangarfield
dangarfield

Reputation: 2330

.forEach does not play with async behaviour, it expects a synchronous function (see here).

Instead, ensure that your getRepositoryLanguages function is async and use a simpler iterator such as a standard for.. of.. loop.

Code example below.

const doSomethingAsync = (repoName) => {
    return new Promise(resolve => setTimeout(resolve(repoName+'Lang'), 100));
}

const getRepositoryLanguages = async (owner, repos) => {
  let stats = []
  for (const repoName of repos) {
    const repoLang = await doSomethingAsync(repoName)
    stats.push(repoLang)
  }
  return stats
}
const init = async () => {
    const data = await getRepositoryLanguages('username', ['repo1','repo2'])
  console.log('data', data)
}
init()

If you're not worried about rate limiting, and wanted to do something in parallel you could Promise.all like this:

const doSomethingAsync = (repoName) => {
  console.log('doSomethingAsync START', repoName)
  return new Promise(resolve => setTimeout(() => {
    console.log('doSomethingAsync END', repoName)
    resolve(repoName + 'Lang')
  }, 1000));
}

const getRepositoryLanguages = async(owner, repos) => {
  const stats = await Promise.all(repos.map(async(repoName) => {
    return doSomethingAsync(repoName)
  }))
  return stats
}
const init = async() => {
  const data = await getRepositoryLanguages('username', ['repo1', 'repo2'])
  console.log('data', data)
}
init()

Upvotes: 2

ezhupa99
ezhupa99

Reputation: 2283

First info:

ForEach loop doesn't stop the execution of the loop even if it has async/await operation in it -> Convert it to normal For Loop for(let i = 0 ....)

/**
 * 
 * @param owner Owner of repository
 * @param repos All his public repos
 * @returns Language statistics
 */
async function getRepositoryLanguages(owner, repos) {

  /**
   * language statistics
   */
  let stats = [];

  for (let index = 0; index < repos.length; index++) {
    const repoName = repos[index];

    const response = await axios.get(
      `https://api.github.com/repos/${owner}/${repoName}/languages`, {
        headers: {
          Authorization: "token ${token}",
        },
      }
    )

    stats.push({
      name: repoName,
      languages: response.data
    });
  };

  return stats;
}

getRepositoryNames(username).then(async(repos) => {
  console.log(await getRepositoryLanguages(username, repos));
});

Upvotes: 1

Related Questions