简体   繁体   English

在JavaScript中使forEach异步

[英]Make forEach asynchronous in JavaScript

I'm trying to understand the asynchronous programming Node.js but stalled on this code. 我正在尝试理解异步编程Node.js但在此代码上停滞不前。

This function in their callback returns an array of files in a directory: 它们的回调中的这个函数返回一个目录中的文件数组:

function openDir(path, callback) {
    path = __dirname + path;
    fs.exists(path, function (exists) {
        if (exists) {
            fs.readdir(path, function (err, files) {
                if (err) {
                    throw err;
                }
                var result = [];
                files.forEach(function (filename, index) {
                    result[index] = filename;
                });
                return callback(result);
            });
        }
    });
}

But when I use asynchronous code inside .forEach , it returns nothing: 但是当我在.forEach使用异步代码时,它什么都不返回:

function openDir(path, callback) {
    path = __dirname + path;
    fs.exists(path, function (exists) {
        if (exists) {
            fs.readdir(path, function (err, files) {
                if (err) {
                    throw err;
                }
                var result = [];
                files.forEach(function (filename, index) {
                    fs.stat(path + filename, function (err, stats) {
                        if (err) {
                            throw err;
                        }
                        result[index] = filename;
                    });
                });
                return callback(result);
            });
        }
    });
}

I understand why it happens, but don't understand how to write correct code. 我理解它为什么会发生,但不明白如何编写正确的代码。

The issue is that fs.stat is also async, but you could probably do something like: 问题是fs.stat也是异步的,但你可以做类似的事情:

var result = [],
    expectedLoadCount = files.length,
    loadCount = 0;

files.forEach(function (filename, index) {
    fs.stat(path + filename, function (err, stats) {
        if (err) {
            throw err;
        }
        result[index] = filename;
        if (++loadCount === expectedLoadCount) callback(result);
    });
});

The other answers may work well, but they are currently quite different semantically from the original code: they both execute stats in parallel, rather than sequentially. 其他答案可能效果很好,但它们在语义上与原始代码完全不同:它们都是并行执行stats ,而不是按顺序执行。 The forEach will initiate as many asynchronous stats operation as there are files in the list of files. forEach将启动与文件列表中的文件一样多的异步stats操作。 The completion order of those operations may quite well be different from the original order of the list. 这些操作的完成顺序可能与列表的原始顺序完全不同。 This may substantially affect the error handling logic. 这可能会严重影响错误处理逻辑。

The following approach implements a state machine, which is aimed to executes stats asynchronously, yet sequentially (untested): 以下方法实现了一个状态机,旨在异步执行stats ,但是按顺序(未经测试):

function openDir(path, callback) {
    path = __dirname + path;
    fs.exists(path, function (exists) {
        if (!exists)
            callback(null, null); // node (err, result) convention
        else {
            fs.readdir(path, function (err, files) {
                if (err)
                    callback(err, null); // node (err, result) convention
                else {
                    var results = [];
                    var i = 0;
                    nextStep(); // process the first file (the first step)

                    function nextStep() {
                        if (i >= files.length) // no more files?
                            callback(null, result); // node (err, result) convention
                        else {
                            fs.stat(path + files[i], function (err, stats) {
                                if (err)
                                    callback(err, null); // node (err, result) convention
                                else {
                                    results[i++] = stats;
                                    // proceed to the next file
                                    nextStep();
                                }
                            });
                        }
                    }
                }
            }
        }
    });                   
});

Promises may help to reduce the nesting level of the famous "Pyramid of Doom" like above. 承诺可能有助于降低上述着名的“末日金字塔”的筑巢水平。

try this: 尝试这个:

function openDir(path, callback) {
    path = __dirname + path;
    fs.exists(path, function (exists) {
        var totalFiles = 0;;
        if (exists) {
            fs.readdir(path, function (err, files) {
                if (err) {
                    throw err;
                }
                var result = [];
                files.forEach(function (filename, index) {
                    fs.stat(path + filename, function (err, stats) {
                        if (err) {
                            throw err;
                        }
                        result[index] = filename;
                        totalFiles++;
                        if(totalFiles === files.length){
                            callback(result);
                        }
                    });
                });
            });
        }
    });
}

you can also use the Async module , to help on these kinds of situations 您也可以使用Async模块来帮助解决这些问题

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM