简体   繁体   English

通过github API异步递归以获取文件

[英]Recursing asyncronously through github API to get files

I'm using the github API to traverse a repo and get a list of all files in it. 我正在使用github API遍历一个repo并获取其中所有文件的列表。 This structure is called a "tree". 这种结构称为“树”。 A tree is basically a subdirectory. 树基本上是一个子目录。 So if I want see the contents of a tree, I need to make a GET request to the ID of that tree. 因此,如果我想查看树的内容,我需要对该树的ID发出GET请求。 The response will be an array of objects representing items in that tree. 响应将是表示该树中的项目的对象数组。 But some of these items will be trees too, so I'll have to make another get request to that tree. 但是这些项目中的一些也将是树,所以我将不得不向该树提出另一个请求。 A repo may look like this: 回购可能如下所示:

|src
    app.jsx
    container.jsx
    |client
        index.html
readme.md

This structure would be represented by the following objects 该结构将由以下对象表示

[
    { 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 }
]

What I need to do is write a function that will return an array of the names of all files. 我需要做的是编写一个函数,它将返回所有文件名称的数组。 This gets pretty tricky, as I'm trying to combine async calls with recursion. 这非常棘手,因为我正在尝试将异步调用与递归相结合。 This is my attempt so far: 这是我到目前为止的尝试:

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

It's crawling through everything, but the promArr is resolving too quickly. 它正在爬行一切,但promArr解析得太快了。 Also, I'm not sure what to pass into resolve. 另外,我不确定要解决什么问题。 Halp me. 打嗝我

Solution 1: 解决方案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();

Solution 2 (preferred): 解决方案2(首选):

Use async and await keywords of ES2017. 使用async等待 ES2017的关键字。

Documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function 文档: 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();

Interesting problem. 有趣的问题。 The reason why promArr is resolving too quickly, as you might have guessed, is because as soon as you push one Promise into it, Promise.all passes its condition and it doesn't wait around for the other Promises to populate in the array. 正如你可能已经猜到的那样, promArr解析得太快的原因是因为只要你将一个Promise推入其中, Promise.all传递它的条件而不会等待其他Promise填充到数组中。

I would try re-writing it so that your recursive function helper accepts two params, tree and arr - with arr being your array of Promises. 我会尝试重写它,以便你的递归函数helper接受两个参数, treearr - arr是你的Promises数组。 You start by calling the function with helper(tree, []) - and inside, you populate the array with the necessary promises and re-call helper with helper(newTree, updatedArray) . 首先使用helper(tree, [])调用该函数 - 然后在内部,使用必要的promises填充数组,并使用helper(newTree, updatedArray)重新调用helper。 Add some logic that identifies when you are done populating promises into updatedArray , and on that condition, just return the updatedArray full of your Promises. 添加一些逻辑,用于标识何时完成将promises填充到updatedArray ,并且在这种情况下,只需返回updatedArray ,其中updatedArray

Then just call Promise.all(helper(tree, [])).then(...) and it should work as intended. 然后只需调用Promise.all(helper(tree, [])).then(...)它应该按预期工作。

Kinda just talked through it but happy to implement some code if you need it. 有点刚谈过,但很高兴在需要时实现一些代码。

Lets mimic the Git database. 让我们模仿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])}
              };

There is also a getDir method included in this library which is asynchronous and would return a directory in say 250ms. 此库中还包含一个getDir方法,该方法是异步的,并且将返回250ms的目录。 I assume gitFake.getDir(id,cb) , where the callback it takes is error first type like cb(err,data) , is not promisified. 我假设gitFake.getDir(id,cb) ,它所采用的回调是错误的第一个类型,如cb(err,data) ,不会被默认。 Lets invent a promisifier for async functions those accept error first type callback; 让我们为异步函数发明一个promisifier,接受错误的第一类回调;

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

Now lets create our recursive asynchronous function getAllDirs to list all nested directories; 现在让我们创建递归异步函数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; } 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM