当一个Node.JS函数中有大量的异步操作时,如何判断所有回调均已完成就成为一个问题。以递归读取一个目录下的所有文件为例,可以有两种方法来判断异步函数是否全部结束:
Promise 方法
Promise有一个 all 方法:
Promise.all(iterable) 方法指当所有在可迭代参数中的 promises 已完成,或者第一个传递的 promise(指 reject)失败时,返回 promise。
实现代码如下所示,来自 William17:
var fs = require('fs');
var readdir = promisify(fs.readdir);
var stat = promisify(fs.stat);
var readFile = promisify(fs.readFile);
// 简单实现一个promisify
function promisify(fn) {
return function() {
var args = arguments;
return new Promise(function(resolve, reject) {
[].push.call(args, function(err, result) {
if(err) {
console.log(err)
reject(err);
}else {
resolve(result);
}
});
fn.apply(null, args);
});
}
}
function readDirRecur(file, callback) {
return readdir(file).then((files) => {
files = files.map((item) => {
var fullPath = file + '/' + item;
return stat(fullPath).then((stats) => {
if (stats.isDirectory()) {
return readDirRecur(fullPath, callback);
} else {
/*not use ignore files*/
if(item[0] == '.'){
//console.log(item + ' is a hide file.');
} else {
callback && callback(fullPath)
}
}
})
});
return Promise.all(files);
});
}
var fileList = []
var timeStart = new Date()
readDirRecur('D:/github/oncedoc', function(filePath) {
fileList.push(filePath)
}).then(function() {
console.log('done', new Date() - timeStart);
console.log(fileList);
}).catch(function(err) {
console.log(err);
});
计数方法:
计数是比较常用的一种方法,其原理类似于c/c++中的引用计数,实现起来比较容易:
var fs = require('fs');
function readDirRecur(folder, callback) {
fs.readdir(folder, function(err, files) {
var count = 0
var checkEnd = function() {
++count == files.length && callback()
}
files.forEach(function(file) {
var fullPath = folder + '/' + file;
fs.stat(fullPath, function(err, stats) {
if (stats.isDirectory()) {
return readDirRecur(fullPath, checkEnd);
} else {
/*not use ignore files*/
if(file[0] == '.') {
} else {
fileList.push(fullPath)
}
checkEnd()
}
})
})
//为空时直接回调
files.length === 0 && callback()
})
}
var fileList = []
var timeStart = new Date()
readDirRecur('D:/github/oncedoc', function(filePath) {
console.log('done', new Date() - timeStart);
console.log(fileList);
})
测试结果比较:
Promise法,耗时 6.2 秒 左右
$ node test.promise.js
done 6214
[ ...
... 55621 more items ]
计数法,耗时 3.5秒左右
$ node test.count.js
done 3590
[ ...
... 55621 more items ]
由此可见,计数法比Promise快将近1倍左右。
没意义,还占用CPU
这个函数没有问题吗,假设第一个是文件,第二个是文件夹,第三个是文件,那么当遍历到文件夹直接return递归文件夹内的文件,那外层的第三个文件不就没被push进数组了?
@薛劝门 #1
好像真的是哎
@薛劝门 #1
不会的,这个foreach会继续运行下去的