【问题标题】:Recursing asyncronously through github API to get files通过github API异步递归获取文件
【发布时间】:2018-02-02 17:20:44
【问题描述】:

我正在使用 github API 来遍历 repo 并获取其中所有文件的列表。这种结构称为“树”。树基本上是一个子目录。因此,如果我想查看一棵树的内容,我需要对该树的 ID 发出 GET 请求。响应将是表示该树中项目的对象数组。但是其中一些项目也将是树,所以我必须向该树发出另一个获取请求。一个 repo 可能如下所示:

|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 解决得太快了。另外,我不确定要解决什么问题。拦住我。

【问题讨论】:

  • 你的意思是//a GET req to the id of the **third** object would return the following array:... -> ... [ { name:'index.html', type:'file', id:44444422 } ]?
  • @redu yes ty 已编辑

标签: javascript node.js github promise github-api


【解决方案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();

解决方案 2(首选):

使用 ES2017 的 asyncawait 关键字。

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

【讨论】:

    【解决方案2】:

    有趣的问题。正如您可能已经猜到的那样,promArr 解决得太快的原因是,只要您将一个 Promise 推入其中,Promise.all 就会通过它的条件并且它不会等待其他 Promise 填充在数组中。

    我会尝试重写它,以便您的递归函数 helper 接受两个参数,treearr - arr 是您的 Promise 数组。您首先使用helper(tree, []) 调用该函数 - 在内部,您使用必要的promise 填充数组并使用helper(newTree, updatedArray) 重新调用帮助器。添加一些逻辑来识别您何时完成将 Promise 填充到 updatedArray 中,在这种情况下,只需返回充满您的 Promises 的 updatedArray

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

    Kinda 刚刚讨论过,但如果您需要,很乐意实现一些代码。

    【讨论】:

      【解决方案3】:

      让我们模仿 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 方法,它是异步的,会在 250 毫秒内返回一个目录。我假设gitFake.getDir(id,cb),它需要的回调是错误第一类型,如cb(err,data),没有被承诺。让我们为接受错误优先类型回调的异步函数发明一个承诺者;

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

      【讨论】:

        猜你喜欢
        • 2012-03-05
        • 1970-01-01
        • 2013-06-19
        • 2017-10-28
        • 1970-01-01
        • 2021-12-23
        • 2014-09-21
        • 2017-06-12
        相关资源
        最近更新 更多