#javascript #reflection #recursion #node.js
#javascript #отражение #рекурсия #node.js
Вопрос:
У меня есть функция, которая выполняет поиск в ширину по большому графику. В настоящее время приложение запускается и завершается через некоторое время. Я хочу добавить finished
событие в EventEmitter.
Моей первой идеей было реализовать счетчик для каждого Recursive
процесса. Но это может привести к сбою, если какой-то Recursive
процесс не вызовет counter--
метод.
var App = function(start, cb) {
var Recursive = function(a, cb) {
// **asynchronous** and recursive breadth-first search
}
var eventEmitter = new EventEmitter();
cb(eventEmitter);
Recursive(start);
};
Как я могу отправить finished
сообщение, если все Recursive
функции завершены.
Редактировать Приложение не выполняет поиск чего-либо на графике, оно должно пройти весь график, чтобы завершить. И неизвестно, сколько элементов в графике.
Edit2 Что-то вроде вычислительного отражения было бы идеально, но, похоже, этого не существует в javascript.
График очень нестабилен, и я выполняю несколько вложенных асинхронных вызовов, которые все могут завершиться неудачей. Есть ли способ узнать, когда все асинхронные рекурсивные вызовы завершены без использования счетчика?
Комментарии:
1. @Atticus: Странно, я не так это понимаю.
2. Вопрос действительно неясен!! Почему вы не можете выполнить выдачу в последней строке вашей функции после
Recursive(start)
?3. После вызова Recursive (start) приложение технически завершено. Но рекурсивные функции все еще вычисляются. Я хочу выдать finished после завершения всех рекурсивных функций
4. Я, вероятно, что-то серьезно недопонимаю, но, на мой взгляд, запуск события в строке после
Recursive(start);
означает, что оно будет выполнено только после возврата функции, т. е. после завершения всей рекурсии. Чего я не понимаю? Здесь нет многопоточности…5. Если вам нужна помощь, я предлагаю вам опубликовать
Recursive
код функции либо здесь, либо, если он слишком большой, здесь: gist.github.com
Ответ №1:
JavaScript является однопоточным.
Таким образом, если Recursive(start);
в нем нет асинхронных вызовов, таких как setTimeout
или ajax
, безопасно просто запускать ваше событие finished после вызова рекурсивной функции.
Общие асинхронные API передают done
функцию.
Таким образом, у вас было бы
Recursive(start, function() {
// trigger finished.
});
var Recursive = function(a, done) {
...
};
И это зависит от пользователей, которые будут вызывать done
, когда они закончат.
Комментарии:
1. рекурсивная функция также переходит в цикл событий — итак, как вы узнаете, когда вызывать done ?
Ответ №2:
Попробуйте что-то вроде этого :
var App = function(start, cb) {
var pendingRecursive = 0;
var eventEmitter = new EventEmitter();
cb(eventEmitter);
var Recursive = function(a) {
// breadth-first search recursion
// before each recursive call:
pendingRecursive ;
Recursive(/*whatever*/);
// at the end of the function
if (--pendingRecursive == 0){
eventEmitter.emit('end');
}
}
pendingRecursive = 1;
Recursive(start);
};
По сути, вы просто увеличиваете счетчик перед каждым рекурсивным вызовом и уменьшаете его в конце вызова, таким образом, вы эффективно подсчитываете количество незавершенных вызовов, когда оно равно нулю, вы можете затем выдать свое событие.
Комментарии:
1. что произойдет, если, учитывая, что это асинхронно,
Recursive
функция вызывается два раза, и эти два вызова завершаются до того, как вызывается третий раз? Я бы сказал, что это не работает. Вы предполагаете, что вызовы сначала выполняются все вместе, и я бы сказал, что это работает в большинстве случаев. Но в моем случае, для файлов и подкаталогов в огромном каталоге, где рекурсивный вызов вызывается в каждом подкаталоге после перечисления файлов и каталогов, этот метод, я предполагаю, не сработает, потому что счетчик может достичь0
даже до завершения перечисления, поскольку вы предполагаете, что он работает как стек.
Ответ №3:
Попробуйте что-то подобное на основе ответа Адриенса
/**
* Function to search for a file recursively from a base directory
* returns an array of absolute paths for files that match the search
* criteria
*/
let recursiveFileSearch = ( baseDir, fileId ) => {
let pathsArray = [];
pendingRecursive = 1;
//recursive funcion to get all config paths
let getFilePaths = ( baseDir ) => {
//require inbuilt path and filesystem modules
let path = require ( 'path' );
let fs = require ( 'fs' );
//read the files in the base directory
let files = fs.readdirSync ( baseDir );
//fetch all config files recursively
for ( let i = 0 ; i < files.length; i ) {
let file = files [ i ];
let filePath = path.resolve ( baseDir, file );
//get file stats
let fileStats = fs.lstatSync ( filePath );
let isFile = fileStats.isFile ( );
let isDir = fileStats.isDirectory ( );
if ( isFile amp;amp; file === fileId ) {
pathsArray.push ( filePath );
}
if ( isDir ) {
pendingRecursive ;
getFilePaths( filePath );
}
}
//decrement the recursive flag
if (--pendingRecursive == 0){
return pathsArray;
}
};
return getFilePaths ( baseDir );
};
//Testing the recursive search
let baseDir = __dirname;
let filePaths = recursiveFileSearch ( baseDir, "your file name" );
Ответ №4:
Можете ли вы использовать логическое значение вне функции в качестве флага и изменять его значение при достижении вашего целевого узла? Возможно, ваши рекурсивные случаи могут находиться в пределах регистра в логическом значении, и когда узел найден, вы можете обновить его значение… Или вы спрашиваете, каков базовый вариант завершения вашей рекурсивной функции?
Комментарии:
1. это скорее обход всего графика, чем поиск. я спрашиваю, как узнать, завершены ли все рекурсивные вызовы в приложении
2. справа — ширина первого дочернего элемента -> братья и сестры -> дочерний элемент -> братья и сестры, повторите, верно? Поэтому я бы использовал уменьшающийся счетчик общего количества узлов, уменьшая каждое посещение. Как только 0, готово, в противном случае поднимите флаг, что узел найден.. сработает ли это для вашего поиска?