Итак, у меня есть следующий код, который пытается рекурсивно найти файлы в репозитории Github.
Структура папок выглядит так
master:
master:gpapi/
master:Documentation/
master:Documentation/auth/
master:Documentation/download/
master:Documentation/search/
Я ожидал, что результат будет выглядеть так:
(master: call) -> master:Documentation/ is a tree!
(master:Documentation/ call) -> master:Documentation/auth/ is a tree!
(master:Documentation/auth/ call) -> master:Documentation/auth/ contents
(master:Documentation/ call) -> master:Documentation/download/ is a tree!
(master:Documentation/download/ call) -> master:Documentation/download/ contents
(master:Documentation/ call) -> master:Documentation/search/ is a tree!
(master:Documentation/search/ call) -> master:Documentation/search/ contents
(master:Documentation/ call) -> master:Documentation/ contents
(master: call) -> master:gpapi/ is a tree!
(master:gpapi/ call) -> master:gpapi/ contents
(master: call) -> master: contents
Вместо этого результат выглядел так:
(master: call) -> master:Documentation/ is a tree!
(master: call) -> master:gpapi/ is a tree!
(master: call) -> master: contents
(master:Documentation/ call) -> master:Documentation/auth/ is a tree!
(master:Documentation/ call) -> master:Documentation/download/ is a tree!
(master:Documentation/ call) -> master:Documentation/search/ is a tree!
(master:Documentation/ call) -> master:Documentation/ contents
(master:gpapi/ call) -> master:gpapi/ contents
(master:Documentation/download/ call) -> master:Documentation/download/ contents
(master:Documentation/search/ call) -> master:Documentation/search/ contents
(master:Documentation/auth/ call) -> master:Documentation/auth/ contents
Как первый вызов функции оценивает каталог / дерево gpapi до того, как самые последние вызовы смогут оценить подкаталоги / деревья документации? Проблема в том, как я делаю рекурсию, или в том, как работает консольное ведение журнала?
Вот код
//Package to connect to GitHub's GraphQL API
var GithubGraphQLApi = require('node-github-graphql')
//Package to edit SQL db
var sql = require('mysql');
//Connect to GitHub API
var github = new GithubGraphQLApi({
token: process.env.GITHUB_API_TOKEN,
debug: true
})
//Call the recursive function
populatefiles("googleplay-api", "master:");
//Find all the files in a repo for a given path
function populatefiles(repo_name, path){
//Query the GitHub API for all files in repo <repo_name>
//found at <path>
github.query(`
{
viewer {
repository(name: "` + repo_name +`") {
object(expression: "` + path +`") {
... on Tree{
entries{
name
type
mode
}
}
}
}
}
}
`, null, (res, err) => {
//The array of files returned by Github
var entries = res.data.viewer.repository.object.entries
for (var e in entries){
var entry = entries[e];
//If the entry is a directory("tree")
if (entry.type === "tree"){
//update the path and log it
var newpath = path + entry.name + "/";
console.info(newpath + " is a tree!");
//call the function with the new path
populatefiles(repo_name, newpath);
}
}
//log all entries found at <path>
console.info(path + " contents");
//console.info(JSON.stringify(res, null, 2))
})
}



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Вы имеете дело с обещаниями, а обещания асинхронны. Чтобы заставить их работать синхронно, вы должны «связать» их вместе с помощью .then() или чего-то подобного.
Я не тестировал этот код, но это общая идея. Вы захотите присвоить возвращаемое значение github.query(), которое будет обещанием. Тогда вы можете использовать .then(). В цикле for все становится немного интереснее.
То, как я это делаю здесь (помещая все обещания в массив, а затем вызывая Promise.all, вы не можете быть уверены в порядке каждой итерации цикла for. Если вам нужно быть уверенным в порядке, вы Мне также нужно было связать их в цепочку. Я вставил образец кода для этого в комментарии (см. строки pr2).
//Call the recursive function
populatefiles("googleplay-api", "master:")
.then(x => console.info('all done'));
.catch(e => console.info('error!', e));
//Find all the files in a repo for a given path
function populatefiles(repo_name, path) {
//Query the GitHub API for all files in repo <repo_name>
//found at <path>
var pr = github.query(`
{
viewer {
repository(name: "` + repo_name + `") {
object(expression: "` + path + `") {
... on Tree{
entries{
name
type
mode
}
}
}
}
}
}
`);
pr = pr.then(res => {
//The array of files returned by Github
var entries = res.data.viewer.repository.object.entries
var prarr = [];
// var pr2 = Promise.resolve(); // create a resolved promise to start the chain, if you're worried about order of the for loop.
for (var e in entries) {
var entry = entries[e];
//If the entry is a directory("tree")
if (entry.type === "tree") {
//update the path and log it
var newpath = path + entry.name + "/";
console.info(newpath + " is a tree!");
//call the function with the new path
prarr.push(populatefiles(repo_name, newpath));
// pr2 = pr2.then(populatefiles(repo_name, newpath)); // if you're worried about order of the loop
}
}
//log all entries found at <path>
console.info(path + " contents");
//console.info(JSON.stringify(res, null, 2))
return Promise.all(prarr);
//return pr2; // if you're worried about the order of the loop
});
return pr;
}
* Edit: Еще одна вещь, которую я забыл упомянуть: с обещаниями вы всегда хотите иметь хотя бы один .catch() в конце цепочки на случай ошибки. Если вы этого не сделаете, Node станет очень сварливым и предупредит вас о невыполненных обещаниях.