javascript - 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(),如果这是您在其中处理承诺的方式的功能。
这就是您完成这两项任务的方式吗?任何帮助深表感谢。谢谢!
解决方案
当您可以一起运行许多异步操作(由 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);
});
推荐阅读
- unity3d - OverlapBoxAll 2d 没有检测到任何东西
- r - 仅当行重复时才删除特定列中包含 NA 的行(基于其他两列)
- c++ - 当我包含 SoftTimer 库时,为什么 Arduino IDE 会给我一个编译错误?
- firebase - 使用 Firebase 流作为 Flutter 中另一个流的输入?
- clojure - 为什么在 Clojure 中将元数据添加到函数定义与其他形式的工作方式不同?
- bash - 为什么我的 if 语句将我的变量评估为空?
- javascript - JS 将对象数组转换为 JSON GroupBy 格式
- python - 线程上的 SQLAlchemy scoped_session 更新不会提交
- python - Heroku上的Flask App在部署后出现应用程序错误
- javascript - React 如何从数组中删除一个对象