2017-08-24 89 views
4

我正在使用github API遍歷repo並獲取其中的所有文件的列表。這個結構被稱爲「樹」。一棵樹基本上是一個子目錄。所以如果我想看一棵樹的內容,我需要對該樹的ID作出GET請求。響應將是表示該樹中項目的對象數組。但其中一些項目也會樹木,所以我必須再次向該樹發送請求。回購可能看起來像這樣:通過github API異步遞歸獲取文件

|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

你的意思是'//一個GET REQ到** **第三對象的ID將返回以下數組:'... - > ...'[{ name:'index.html',輸入:'file',id:44444422}]'? – Redu

+0

@redu是ty編輯 –

回答

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的關鍵字。

文檔: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(); 
+0

這就像一個魅力ty! –

1

有趣的問題。正如您可能已經猜到的那樣,promArr解決得太快的原因是因爲只要您將其中一個Promise放入其中,Promise.all就會通過它的條件,並且不會等待其他Promise填充到數組中。

我會嘗試重寫它讓你的遞歸函數helper接受兩個參數,treearr - 與arr爲您承諾的陣列。你首先調用函數helper(tree, []) - 在裏面,你用必要的承諾填充數組,然後用helper(newTree, updatedArray)重新調用幫助器。在updatedArray中添加一些標識完成承諾的邏輯,在這種情況下,只需返回您的承諾中的updatedArray即可。

然後只需撥打Promise.all(helper(tree, [])).then(...),它應該按預期工作。

有點剛剛通過它,但很高興實施一些代碼,如果你需要它。

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

還有包括在這個庫是異步的,並且會在250毫秒的發言權返回目錄getDir方法。我假設gitFake.getDir(id,cb),其中回調所需的錯誤是第一種類型,如cb(err,data),不是promisified。讓我們爲異步函數發明一個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; }