Определить, когда рекурсивная функция завершена

#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, готово, в противном случае поднимите флаг, что узел найден.. сработает ли это для вашего поиска?