我正在使用github API遍历一个repo并获取其中所有文件的列表。这种结构称为“树”。树基本上是一个子目录。因此,如果我想查看树的内容,我需要对该树的ID发出GET请求。响应将是表示该树中的项目的对象数组。但是这些项目中的一些也将是树,所以我将不得不向该树提出另一个请求。回购可能如下所示:
|src
app.jsx
container.jsx
|client
index.html
readme.md
此结构将由以下对象
表示[
{ name:'src', type:'tree', id:43433432 },
{ name:'readme.md', type:'md', id:45489898 }
]
//a GET req to the id of the first object would return the following array:
[
{ name:'app.jsx', type:'file', id:57473738 },
{ name:'contain.jsx', type:'file', id:748433454 },
{ name:'client', type:'tree', id:87654433 }
]
//a GET req to the id of the third object would return the following array:
[
{ name:'index.html', type:'file', id:44444422 }
]
我需要做的是编写一个函数,它将返回所有文件名称的数组。这非常棘手,因为我正在尝试将异步调用与递归相结合。这是我到目前为止的尝试:
function treeRecurse(tree) {
let promArr = [];
function helper(tree) {
tree.forEach(file => {
let prom = new Promise((resolve, reject) => {
if (file.type == `tree`) {
let uri = treeTrunk + file.sha + `?access_token=${config.ACCESS_TOKEN}`;
request({ uri, method: 'GET' })
.then(res => {
let newTree = JSON.parse(res.body).tree;
resolve(helper(newTree));
});
} else resolve(promArr.push(file.path));
promArr.push(prom);
});
});
};
helper(tree);
Promise.all(promArr)
.then(resArr => console.log(`treeRecurse - resArr:`, resArr));
};
它正在浏览所有内容,但promArr
解析得太快了。另外,我不确定要解决什么问题。哄我。
答案 0 :(得分:2)
解决方案1:
let username = 'YOUR_USERNAME';
let reponame = 'YOUR_REPONAME';
let access_token = 'YOUR_ACCESS_TOKEN';
const axios = require('axios');
let tree = [];
let fileNames = [];
function start() {
axios.get(`https://api.github.com/repos/${username}/${reponame}/git/trees/master?access_token=${access_token}`)
.then(
function(res) {
tree = tree.concat(res.data.tree);
getFilesNameRecur();
},
function(err) {
console.log(err);
}
);
}
function getFilesNameRecur() {
if (tree.length !== 0) {
let firstObjectOfTree = tree.pop();
if (firstObjectOfTree.type === 'tree') {
axios.get(firstObjectOfTree.url + `?access_token=${access_token}`)
.then(
function(response) {
tree = tree.concat(response.data.tree);
getFilesNameRecur();
},
function(err) {
console.log(err);
}
);
} else if (firstObjectOfTree.type === 'blob') {
fileNames.push(firstObjectOfTree.path);
getFilesNameRecur();
}
} else {
// Finished fetching all file names
console.log(fileNames);
}
}
start();
解决方案2(首选):
使用ES2017的 async 和等待关键字。
文档:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
import axios from 'axios';
let username = 'YOUR_USERNAME';
let reponame = 'YOUR_REPONAME';
let access_token = 'YOUR_ACCESS_TOKEN';
let tree = [];
let fileNames = [];
async function start() {
try {
let res = await axios.get(`https://api.github.com/repos/${username}/${reponame}/git/trees/master?access_token=${access_token}`);
tree = tree.concat(res.data.tree);
while (tree.length !== 0) {
await getFilesNameRecur();
}
console.log(fileNames);
} catch(e) {
console.log(e);
}
}
async function getTreeFromGithub(url) {
try{
let response = await axios.get(url + `?access_token=${access_token}`);
return response.data.tree;
} catch (e) {
console.log(e);
throw e;
}
}
async function getFilesNameRecur() {
let firstObjectOfTree = tree.pop();
if (firstObjectOfTree.type === 'tree') {
let subTree = await getTreeFromGithub(firstObjectOfTree.url);
tree = tree.concat(subTree);
} else if (firstObjectOfTree.type === 'blob') {
fileNames.push(firstObjectOfTree.path);
}
}
start();
答案 1 :(得分:1)
有趣的问题。正如您可能已经猜到的那样,promArr
解析过快的原因是因为只要您将一个Promise
推入其中,Promise.all
就会通过其条件并且不会等待其他Promise填充在数组中。
我会尝试重新编写它,以便您的递归函数helper
接受两个参数tree
和arr
- arr
是您的Promises数组。首先使用helper(tree, [])
调用函数 - 然后在内部,使用必要的promises填充数组,并使用helper(newTree, updatedArray)
重新调用帮助程序。添加一些逻辑,用于标识何时完成将承诺填充到updatedArray
中,并且在这种情况下,只需返回updatedArray
充满您的承诺。
然后只需致电Promise.all(helper(tree, [])).then(...)
,它应该按预期工作。
有点刚谈过它,但很高兴在你需要的时候实现一些代码。
答案 2 :(得分:1)
让我们模仿Git数据库。
var gitFake = {0 : [{ name:'src', type:'tree', id:43433432 },
{ name:'readme.md', type:'md', id:45489898 }
],
43433432: [ { name:'app.jsx', type:'file', id:57473738 },
{ name:'contain.jsx', type:'file', id:748433454 },
{ name:'client', type:'tree', id:87654433 }
],
87654433: [ { name:'index.html', type:'file', id:44444422 }
],
getDir : function(id,cb){ setTimeout(cb, 250, !this[id] && "Error: No such directory..!", this[id])}
};
此库中还包含一个getDir
方法,该方法是异步的,并且会返回250ms的目录。我假设gitFake.getDir(id,cb)
,其中所采用的回调是错误优先类型,如cb(err,data)
,并未实现。让我们为异步函数发明一个promisifier,接受错误的第一类回调;
function promisify(f){
return data => new Promise((v,x) => f(data, (err,res) => err ? x(err) : v(res)));
}
现在让我们创建递归异步函数getAllDirs
来列出所有嵌套目录;
function promisify(f){ // utility function to promisify the async functions taking error first callback
return data => new Promise((v,x) => f(data, (err,res) => err ? x(err) : v(res)));
}
function getAllDirs(root = 0){
gd(root).then(function(ds){
ds.length && (console.log(ds),
ds.filter( d => d.type === "tree")
.forEach(d => getAllDirs(d.id)));
})
.catch(e => console.log(e));
}
var gitFake = {0 : [{ name:'src', type:'tree', id:43433432 },
{ name:'readme.md', type:'md', id:45489898 }
],
43433432: [ { name:'app.jsx', type:'file', id:57473738 },
{ name:'contain.jsx', type:'file', id:748433454 },
{ name:'client', type:'tree', id:87654433 }
],
87654433: [ { name:'index.html', type:'file', id:44444422 }
],
getDir : function(id,cb){ setTimeout(cb, 250, !this[id] && "Error: No such directory..!", this[id])}
},
gd = promisify(gitFake.getDir.bind(gitFake));
getAllDirs();
.as-console-wrapper { max-height: 100% !important; top: 0; }