首页 > 解决方案 > Javascript:确保一个异步函数在另一个完成之前不会运行;与承诺一起工作

问题描述

我正在从 github 存储库中获取信息。我想获取该仓库中的拉取请求列表,获取与每个拉取请求关联的提交列表,然后对于每个提交,我想获取诸如提交的作者、与每个提交关联的文件数和对每个文件进行的添加和删除次数。我正在使用 axios 和 github API 来完成此操作。我知道如何使用 API,但 Promise 和异步函数使我无法完成任务。我有以下代码:

const axios = require('axios');
var mapOfInformationObjects = new Map();
var listOfCommits = [];
var listOfSHAs = [];
var gitApiPrefix = link I'll use to start fetching data;
var listOfPullRequestDataObjects = [];
var listOfPullRequestNumbers = [];
var mapOfPullNumberToCommits = new Map();

function getAllPullRequests(gitPullRequestApiLink) {
    return new Promise((resolve, reject) => {
        axios.get(gitPullRequestApiLink).then((response) =>{
            listOfPullRequestDataObjects = response['data'];
        var k;
        for (k = 0; k < listOfPullRequestDataObjects.length; k++){
            listOfPullRequestNumbers.push(listOfPullRequestDataObjects[k]['number']);
        }
        resolve(listOfPullRequestNumbers);
    }).catch((error) => {
        reject(error);
    })
})
}

function getCommitsForEachPullRequestNumber(listOfPRNumbers) {
var j;
for (j = 0; j < listOfPRNumbers.length; j++) {
    currPromise = new Promise((resolve, reject) => {
        currentGitApiLink = gitApiPrefix + listOfPRNumbers[j] + "/commits";
        axios.get(currentGitApiLink).then((response) => {
            mapOfPullNumberToCommits.set(listOfPRNumbers[j], response['data']);
            resolve("Done with Pull Request Number: " + listOfPRNumbers[j]);
        }).catch((error) => {
            reject(error);
        })
    })
}

}

function getListOfCommits(gitCommitApiLink){
return new Promise((resolve, reject) => {
    axios.get(gitCommitApiLink).then((response) => {
        resolve(response);
    }).catch((error) => {
        reject(error);
    })
})
}

到目前为止,我做了一些我想按顺序调用的函数。首先我想调用 getAllPullRequestNumbers(someLink) 然后我想调用 getCommitsForEachPullRequestNumber(listofprnumbers) 然后 getListOfCommits(anotherLink)

所以它看起来像

getAllPullRequestNumbers(someLink)
getCommitsForEachPullRequestNumber(listofprnumbers)
getListOfCommits(anotherlink)

但是出现了两个问题:1)我不确定这是否是您调用函数的方式,以便序列中的第一个函数在另一个函数之前完成。2) 因为我不熟悉 Javascript,所以我不确定,尤其是 getCommitsForEachPullRequestNumber 函数,因为您运行一个循环并在循环的每次迭代中调用 axios.get(),如果这是您在其中处理承诺的方式的功能。

这就是您完成这两项任务的方式吗?任何帮助深表感谢。谢谢!

标签: javascriptasynchronouspromiseaxios

解决方案


当您可以一起运行许多异步操作(由 Promise 表示)并且您想知道它们何时全部完成时,您可以使用Promise.all(). 你收集一组 promise 并将其传递给它Promise.all(),它会告诉你它们何时全部完成或其中一个何时触发错误。如果全部完成,Promise.all()将返回一个解析为结果数组的承诺(每个异步操作一个)。

当您迭代一个数组来执行您的一组异步操作时,最好使用它,.map()因为它可以帮助您创建一个可以提供给Promise.all(). 以下是你如何做到这一点getCommitsForEachPullRequestNumber()

function getCommitsForEachPullRequestNumber(listOfPRNumbers) {
    let mapOfPullNumberToCommits = new Map();
    return Promise.all(listOfPRNumbers.map(item => {
        let currentGitApiLink = gitApiPrefix + item + "/commits";
        return axios.get(currentGitApiLink).then(response => {
            // put data into the map
            mapOfPullNumberToCommits.set(item, response.data);
        });
    })).then(() => {
        // make resolved value be the map we created, now that everything is done
        return mapOfPullNumberToCommits;
    });
}

// usage:
getCommitsForEachPullRequestNumber(list).then(results => {
    console.log(results);
}).catch(err => {
    console.log(err);
});

然后,在 中getListOfCommits(),由于 axios 已经返回了一个 Promise,因此没有理由将它包装在手动创建的 Promise 中。也就是说,实际上,考虑一个 Promise 反模式。相反,只需返回 axios 已经返回的承诺。事实上,可能甚至没有理由将它作为一个函数,因为人们可以直接使用它axios.get()来实现相同的结果:

function getListOfCommits(gitCommitApiLink){
    return axios.get(gitCommitApiLink);
}

然后,getAllPullRequests()您似乎只是在打一个axios.get()电话,然后处理结果。可以这样做:

function getAllPullRequests(gitPullRequestApiLink) {
    return axios.get(gitPullRequestApiLink).then(response => {
        let listOfPullRequestDataObjects = response.data;
        return listOfPullRequestDataObjects.map(item => {
            return item.number;
        });
    });
}

现在,如果您尝试按以下顺序依次执行这三个操作:

getAllPullRequests(someLink)
getCommitsForEachPullRequestNumber(listofprnumbers)
getListOfCommits(anotherlink)

您可以将这三个操作的 Promise 链接在一起以对它们进行排序:

 getAllPullRequests(someLink)
   .then(getCommitsForEachPullRequestNumber)
   .then(mapOfPullNumberToCommits => {
       // not entirely sure what you want to do here, perhaps
       // call getListOfCommits on each item in the map?
   }).catch(err => {
      console.log(err);
 });

或者,如果您将此代码放入async函数中,则可以使用async/awit

 async function getAllCommits(someLink) {
      let pullRequests = await getAllPullRequests(someLink);
      let mapOfPullNumberToCommits = await getCommitsForEachPullRequestNumber(pullRequests);
      // then use getlistOfCommits() somehow to process mapOfPullNumberToCommits
      return finalResults;
 }

 getAllCommits.then(finalResults => {
     console.log(finalResults);
 }).catch(err => {
     console.log(err);
 });

推荐阅读