Nodejs中如何知道异步foreach已经执行完毕?

Nodejs中如何知道异步foreach已经执行完毕?

如下有个遍历文件夹的函数,如何才能得知它已经遍历结束了? 谢谢!

function walk(path, floor, handleFile) {
//    handleFile(path, floor);
    floor++;
    fs.readdir(path, function(err, files) {
        if (err) {
            console.log('read dir error');
        } else {
            files.forEach(function(item) {

                var tmpPath =path + '/' + item;
                fs.stat(tmpPath, function(err1, stats,people,factor) {
                    if (err1) {
                        console.log('stat error');
                    } else {
                        if (stats.isDirectory()) {
                            walk(tmpPath, floor, handleFile);
                        } else {
                            handleFile(tmpPath,item, floor);
                        }
                    }
                })
            });
        }
    });
}

13 回复

要解决这个问题,我们需要确保在所有的异步操作完成后能够得到通知。这可以通过使用递归函数的完成条件来实现,或者通过引入一个计数器来跟踪尚未完成的异步操作。

以下是一个改进版本的walk函数,它会在所有异步操作完成后调用一个回调函数:

const fs = require('fs');

function walk(path, floor, handleFile, callback) {
    floor++;
    fs.readdir(path, function(err, files) {
        if (err) {
            console.log('read dir error');
            return callback(err);
        }

        let remainingFiles = files.length;
        if (remainingFiles === 0) {
            return callback(null); // 如果没有文件,直接回调
        }

        files.forEach(function(item) {
            const tmpPath = path + '/' + item;
            fs.stat(tmpPath, function(err1, stats) {
                if (err1) {
                    console.log('stat error');
                    return callback(err1);
                }

                if (stats.isDirectory()) {
                    walk(tmpPath, floor, handleFile, function(err) {
                        if (err) {
                            return callback(err);
                        }
                        remainingFiles--;
                        if (remainingFiles === 0) {
                            callback(null);
                        }
                    });
                } else {
                    handleFile(tmpPath, item, floor);
                    remainingFiles--;
                    if (remainingFiles === 0) {
                        callback(null);
                    }
                }
            });
        });
    });
}

// 使用示例
walk('/some/path', 0, function(path, name, level) {
    console.log(`Processed file: ${path} at level ${level}`);
}, function(err) {
    if (err) {
        console.error('Walk completed with errors:', err);
    } else {
        console.log('Walk completed successfully.');
    }
});

解释

  1. 参数调整walk函数现在接受一个额外的参数callback,用于在遍历完成后调用。
  2. 剩余文件计数器:引入了一个remainingFiles变量来跟踪尚未处理的文件数量。
  3. 递归调用:当遍历到子目录时,递归调用walk函数,并在每次递归调用完成后减少remainingFiles的值。
  4. 完成检查:在所有异步操作完成后(即remainingFiles变为0),调用callback函数。

这样,我们就可以在所有文件和子目录都被处理后得到通知。


async这个库把

如果用wind,该怎么写呢?

水平不够,wind的文档真难看懂。。。突袭到了半夜模仿着写了,心里没底,请指点下,谢谢: 1 这么样用异步函数+异步流程控制库,和直接用同步函数+个nexttick有什么区别? 2 有forEach、递归的方法,如何既利用异步的高效性,又能得到全部执行完毕的点?

var fs = require(‘fs’); var Wind = require(‘Wind’); var Binding = Wind.Async.Binding;

var myPath = ‘e:/nodejs/home/public’; fs.readdirAsync = Binding.fromStandard(fs.readdir); fs.statAsync = Binding.fromStandard(fs.stat);

var walk = eval(Wind.compile(“async”,function (path, floor, handleFile) { floor++;

var myReadDirResult = $await(fs.readdirAsync(path));

//不知道为什么,forEach不行 for(var i = 0 ; i < myReadDirResult.length ; i++ ){ var item = myReadDirResult[i]; var tmpPath = path + ‘/’ + item; console.log(‘tmpPath:’ + tmpPath);

var myStatResult = $await(fs.statAsync(tmpPath));
if (myStatResult.isDirectory()){
    $await(walk(tmpPath, floor, handleFile).start());
}
else{
    handleFile(tmpPath);
}

} console.log(path + ‘文件夹结束’);

}));

var main = eval(Wind.compile(“async”,function () { $await(walk(myPath, 0, function(filePath){ }).start()); console.log(‘结束’); }));

main().start();

console.log(‘这是最后一行’);

异步坑死人

弄个变量在回调函数里累加,变量的大小等于文件的个数不就知道遍历完成了么

function walk (path, handleFile, callback) {
    var len = 1,       // 文件|目录数,起始一个
        floor = 0;     // 第x个目录?

    function done () {
    // 完成任务, 运行回调函数
        if (--len === 0) {
            callback();
        }
    }

    function composeErr (err) {
    // 错误处理
        console.log('stat error');
        done();  // 以错误内容完成
    }

    function composeDir (path) {
    // 目录处理
        floor++;
        fs.readdir(path, function (err, files) {
            if (err) {
                console.log('read dir error');
                done();  // 目录完成 
                return;
            }
            len += files.length;  // 子文件|子目录计数
            files.forEach(function (filename) {
                compose(path + '/' + filename);  // 子内容新的操作
            });
            done();  // 目录完成
        });
    }
 
    function composeFile (path) {
    // 文件处理
        handleFile(path, floor);
        done();  // 文件完成
    }

    function compose (path) {
        fs.stat(path, function (err, stats) {
            if (err) {
                composeErr(err);
                return;
            }

            if (stats.isDirectory()) {
                composeDir(path);
                return;
            }

            composeFile(path);
        });
    }

    compose(path);
}

async.map([],function(item,call) { //do…全局变量值 call(null, null) },function(err,result){ //do。。。获取变量值 })

2015-11-05 16:20:38 现在别用async了

untitled1.png

#!/usr/bin/env node
'use strict';

const Promise = require(‘bluebird’); const fs = Promise.promisifyAll(require(‘fs’)); const co = require(‘co’);

// example const handleFile = f => f;

const walkAsync = co.wrap(function* (path) { var files = yield fs.readdirAsync(path); var ret = [];

for(let f of files) { let p = path + ‘/’ + f; var s = yield fs.statAsync§;

if (s.isDirectory()) { var r = yield walkAsync§; ret = ret.concat®; } else { ret.push§ handleFile§; } }

return ret; });

co(function *(){ var files = yield walkAsync(__dirname); console.log(files); }).catch(e => console.error(e.stack || e));

不小心用了var…

我想说,foreach不是同步的吗

为了确定异步 forEach 遍历已经完成,可以使用一个计数器来跟踪正在执行的异步操作的数量。当所有操作都完成后,这个计数器将变为零,从而可以执行回调函数。

以下是修改后的示例代码:

const fs = require('fs');

function walk(path, floor, handleFile, callback) {
    floor++;
    let remaining = 0;

    fs.readdir(path, function(err, files) {
        if (err) {
            console.log('read dir error');
            return callback(err);
        }

        files.forEach(function(item) {
            remaining++;

            var tmpPath = path + '/' + item;
            fs.stat(tmpPath, function(err1, stats) {
                if (err1) {
                    console.log('stat error');
                } else {
                    if (stats.isDirectory()) {
                        walk(tmpPath, floor, handleFile);
                    } else {
                        handleFile(tmpPath, item, floor);
                    }
                }
                checkCompletion();
            });

            function checkCompletion() {
                if (--remaining === 0) {
                    callback(); // 所有操作完成时调用回调
                }
            }
        });

        checkCompletion();
    });
}

// 使用示例
walk('/some/path', 0, (filePath, name, depth) => {
    console.log(`File: ${filePath} at depth ${depth}`);
}, () => {
    console.log('Directory traversal completed.');
});

解释:

  1. 计数器:在 walk 函数内部定义了一个名为 remaining 的变量,用于跟踪当前正在进行的异步操作数量。
  2. 递增计数器:每次处理一个文件或目录时,增加 remaining 计数器。
  3. 递减计数器:在每个异步操作完成之后,减少 remaining 计数器。
  4. 检查完成:通过 checkCompletion 函数检查是否所有操作已完成。如果 remaining 变为零,则调用回调函数 callback() 表示所有操作已结束。

这样,我们就能确定所有异步操作都已经完成了。

回到顶部