异步执行递归数据树构造?

时间:2014-05-14 14:31:07

标签: javascript multithreading node.js asynchronous recursion

我正在开发一个使用文件树的Web应用程序。前端JavaScript向我的Node.js服务器执行ajax请求,该服务器调用我的browse2导出函数。然后,该函数负责为我的函数getFolderContents()提供正确的路径,该函数以递归方式构建文件系统层次结构对象结构。

我的问题是我目前正在同步做事。在研究了Node.js的内部工作原理后,我似乎不惜一切代价避免同步操作。因此,我想将我的代码转换为异步工作。但是,我无法使其正常运行,而且我的所有解决方案都令人费解。

我尝试使用" async"来管理流程。包。想弄清楚,我没有运气。我尝试实现自己的计数器/循环/回调系统来确定进程何时完成执行。最终,我想我无法绕过异步执行流程。

我想问两个问题: 1.在这种情况下,同步而不是异步执行此请求是否有害? 2.如果对第一个问题是肯定的,我应该如何将此代码转换为异步?

注意:当我尝试异步执行操作时,我使用了每个同步函数的异步对应函数。

以下是我的同步(工作)代码:

var path = require('path');
var fs = require('fs');

exports.browse2 = function(request, response) {
    var tree = getFolderContents('C:\\Users\\AccountName\\folder1\\folder2\\folder3\\test\\');

    response.send(tree);
};


function getFolderContents(route) {
    var branch = {};
    branch.title = path.basename(route);
    branch.folder = true;
    branch.children = [];

    var files = fs.readdirSync(route);
    var size = files.length;

    for (var i = 0; i < size; i++) {
        var file = files[i];
        var concatPath = path.join(route, file);

        if (fs.lstatSync(concatPath).isDirectory())
            branch.children.push(getFolderContents(concatPath));
        else
            branch.children.push({
                "title" : path.basename(file),
                "path" : file
            });
    }

    return branch;
}

我感谢所有的投入!

编辑:

添加了异步代码尝试。没完全工作。只收到树的一部分。

    exports.browse2 = function(request, response) {
        getFolderContents(
                'C:\\Users\\AccountName\\folder1\\folder2\\folder3\\test\\',
                function(tree) {
                    response.send(tree);
                });
    };

function getFolderContents(route, callback) {
    var branch = {};
    branch.title = path.basename(route);
    branch.folder = true;
    branch.children = [];

    fs.readdir(route, function(err, files) {
        files.forEach(function(file) {
            var concatPath = path.join(route, file);

            fs.lstat(concatPath, function(err, stats) {
                if (stats.isDirectory())
                    branch.children.push(getFolderContents(concatPath, callback));
                else
                    branch.children.push({
                        "title" : path.basename(file),
                        "path" : file
                    });

                callback(branch);
            });         
        });
    });
}

2 个答案:

答案 0 :(得分:1)

您遇到的基本问题是,当您使用异步调用时,您不能仅仅将函数分配给函数返回。异步的全部意义在于函数不会等待。例如:

function get_data(a) {
    var data = some_async_call(a);

    //at this point, data is undefined because execution won't wait on the calls to finish

    data.do_something();  // this breaks because of the above
}

因此,您所做的是将匿名函数传递给称为回调的异步函数,异步函数在操作实际完成后调用该函数。上面的例子将成为这个:

function get_data(a) {
    some_async_call(a, function(data) {
        data.do_something();
    });
}

function some_async_call(variable, callback) {
    call_async({
        data: variable,
        success: callback
    });
}

在你的情况下看起来像这样:

exports.browse2 = function(request, response) {
    getFolderContents('C:\\Users\\AccountName\\folder1\\folder2\\folder3\\test\\', function(tree) {
        response.send(tree);
    });
};

function getFolderContents(route, callback) {
    var branch = {};
    branch.title = path.basename(route);

    ...

    callback(branch);
}

如果您熟悉setTimetout,这就是它的工作原理 - 设计模式是传递一个完成工作的匿名函数,然后在数据/信息实际可用后执行该函数。

答案 1 :(得分:0)

我设法让它发挥作用。以下是我对自己问题的回答:

  1. 最好异步执行任务,因为这样做会导致应用程序阻止其他用户接收响应,直到后续请求被响应为止。

  2. 将同步代码转换为异步代码的方法是使用并行循环。我的具体案例的代码是:

    var path = require('path');
    var fs = require('fs');
    
    exports.browse2 = function(request, response) {
    getFolderContents(
            'C:\\Users\\AccountName\\folder1\\folder2\\folder3\\test\\',
            function(err, tree) {
                if (err)
                    throw err;
                response.send(tree);
            });
    };
    
    function getFolderContents(route, callback) {
    var branch = {};
    branch.title = path.basename(route);
    branch.folder = true;
    branch.children = [];
    
    fs.readdir(route, function(err, files) {
        if (err)
            return callback(err);
        var pending = files.length;
        if (!pending)
            return callback(null, branch);
        files.forEach(function(file) {
            var concatPath = path.join(route, file);
            fs.lstat(concatPath, function(err, stats) {
                if (stats && stats.isDirectory()) {
                    getFolderContents(concatPath, function(err, res) {
                        branch.children.push(res);
                        if (!--pending)
                            callback(null, branch);
                    });
                } else {
                    branch.children.push({
                        "title" : path.basename(file),
                        "path" : file
                    });
                    if (!--pending)
                        callback(null, branch);
                }
            });
        });
    });
    }
    
  3. 感谢用户&#34; chjj&#34;他对这个帖子中的类似问题做出回应:node.js fs.readdir recursive directory search

    感谢用户&#34; Dan Smolinske&#34;引导我到线程。