Node.js - 如何控制异步回调序列?

时间:2015-07-14 09:41:45

标签: javascript node.js asynchronous

在下面的示例中,我将遍历目录树以获取目录和文件的列表。重要的是,我需要目录和文件的顺序与在文件资源管理器中直观地查看目录和文件的顺序相同。为简洁起见,我只是按照我期望的顺序将文件夹和文件写入console.log ......

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

var walk = function(dir) {
    console.log(dir);
    var files = [];
    var items =  fs.readdirSync(dir).sort(function (a, b) {
        return a.toLowerCase().localeCompare(b.toLowerCase());
    });
    items.forEach(function(item){
        if (item.charAt(0) !== '.') {
            var itemPath = path.join(dir,item);
            if (fs.lstatSync(itemPath).isDirectory()) {
                walk(itemPath);
            } else {
                files.push(itemPath);
            }
        }
    });
    files.forEach(function(file){
        console.log(file);
    });
};

walk('c:\\foo\\bar');

这个同步版本花了20分钟来开发和执行我需要的确切序列。

将其转变为异步解决方案是如何使Node.js成为可扩展的#34;与其他语言相比,它可能很快变得冗长,复杂和耗时。

虽然我会对其他人如何以纯粹的方式(强制执行顺序)解决这个问题感兴趣,但我真的很感兴趣其他开发人员如何使用锅炉板库来解决这类问题。

NPM似乎有很多解决方案都采用了自己的实现方式。没有花时间学习它们,我不确定选择哪一个。

底线问题......如何在执行回调序列的同时将上述代码转换为异步解决方案。例子会有所帮助。如果您使用锅炉板库,为什么选择它。我很想听听其他人的评论,尤其是各种解决方案的风格和可读性。

更新1

我需要强制执行递归函数中发生的异步事件的回调序列。这不应与强制执行可以用promises解决的嵌套回调的回调序列相混淆,例如然后()。然后()。然后()...

我需要做的是递归walk()函数一次只触发一个,这样下一个walk()有效地等待前一个walk()完成。我说"有效地等待"作为一个应该发生什么的想法,但显然使代码"真的等待"意味着暂停代码也不是正确的解决方案。

虽然我想知道某个解决方案是否以某种方式实现process.nextTick()以停放下一个walk()可能是一个方向。

2 个答案:

答案 0 :(得分:1)

为什么在涉及异步操作时不应该使用Promises

bluebird example

的简化自定义版本
var Promise = require("bluebird");
var join = Promise.join;
var fs = Promise.promisifyAll(require("fs"));
var path = require("path")

var walk = function(dir){
    return fs.readdirAsync(dir).map(function(fileName) {
        var file = path.join(dir, fileName);
        var stat = fs.statAsync(file);
        return join(stat, function(stat) {
            return {
                stat: stat,
                fileName: file
            }
        });
    }).call("sort", function(a, b) {
        return a.fileName.localeCompare(b.fileName);
    }).each(function(file) {
        if(file.stat.isDirectory()){
            walk(file.fileName);
        }
        console.log(file.fileName + " last modified " + file.stat.mtime)
    })
}

walk('./');

答案 1 :(得分:0)

我重新审视了这个问题并提出了以下解决方案......

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

var sorter = function (a, b) {
    return a.toLowerCase().localeCompare(b.toLowerCase());
}

var readdirAsync = function(dir) {
    return new Promise(function (resolve,reject) {
        fs.readdir(dir,function(err,dirs) {
            if (err) {
                reject(err)
            } else {
                resolve(dirs)
            }
        })
    })
}

var walker = function(dir) {
    return new Promise(function (resolve,reject) {
        var depth = 0;
        var fileTree = [];
        var sortHack=Array(50).join('Z')+':';
        var walk = function(dir) {
            var files = [];
            depth++;
            fileTree.push(dir);
            readdirAsync(dir).then(function(dirs) {
                dirs.forEach(function(item){
                if (item.charAt(0) !== '.') {
                        var itemPath = path.join(dir,item);
                        if (fs.lstatSync(itemPath).isDirectory()) {
                            walk(itemPath);
                        } else {
                            var parts=itemPath.split('/');
                            parts.push(sortHack + parts.pop());
                            files.push(parts.join('/'));
                        }
                    }
                });
                files.forEach(function(file){
                    fileTree.push(file);
                });
                depth--;
                if (depth===0) {
                    fileTree.sort(sorter);
                    for (var n=0;n<fileTree.length;n++) {
                        fileTree[n] = fileTree[n].replace(sortHack,"")
                    }
                    resolve(fileTree);          
                }

            },function(reason){reject(reason)}).catch(function(reason){reject(reason)});
        }
        walk(dir);
    });
}

var start = new Date().getTime();
walker('/home').then(function(tree){
    console.log(tree);
    var took = new Date().getTime() - start;
    console.log(took + 'ms')
},function(reason){
    console.log("something went wrong :( " + reason);
}).catch(function(reason){
    console.log("something went very wrong :S " + reason);
});