通过github APIasynchronousrecursion来获取文件

我正在使用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 } ] 

我需要做的是写一个函数,将返回所有文件的名称数组。 这变得非常棘手,因为我试图将asynchronous调用与recursion结合起来。 这是我迄今的尝试:

 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解决得太快了。 另外,我不确定要通过什么解决。 哈哈我。

解决scheme1:

 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(); 

解决scheme2(首选):

使用asynchronous等待 ES2017的关键字。

文档: 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(); 

有趣的问题。 正如你可能已经猜到的, promArr解决得太快的原因是因为只要你把一个Promise放入它, Promise.all传递它的条件,而不会等待其他的Promise来填充数组。

我会尝试重写它,以便您的recursion函数helper接受两个参数, treearrarr是您的Promise数组。 你首先用helper(tree, [])调用函数,然后在内部用必要的promise来填充数组,然后用helper(newTree, updatedArray)重新调用helper。 添加一些逻辑来标识你什么时候将承诺填充到updatedArray ,在这种情况下,只需返回你的承诺中的updatedArray

然后,只需调用Promise.all(helper(tree, [])).then(...) ,它应该按预期工作。

有点刚刚通过它,但很高兴实施一些代码,如果你需要它。

让我们模仿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方法,它是asynchronous的,并会返回一个250ms的目录。 我假设gitFake.getDir(id,cb) ,它需要的callback是错误第一types如cb(err,data) ,不是promisified。 让我们发明一个asynchronous函数promisifier那些接受错误第一typescallback;

 function promisify(f){ return data => new Promise((v,x) => f(data, (err,res) => err ? x(err) : v(res))); } 

现在让我们创build我们的recursionasynchronous函数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; }