通过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
接受两个参数, tree
和arr
– arr
是您的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; }