Node.js 回调中的发射器空数据
Node.js emitter null data on callback
有一个函数,我用它来读取目录中的所有文件,然后将带有发射器的对象发送到客户端。
我的代码运行良好,
const getFilesList = (path, emitter) => {
fs.readdir(path, (err, files) => {
emitter('getFileList', files);
});
};
但是当我想用这段代码过滤隐藏文件时,'standardFolders' 将在发射器中发送空文件。
const getFilesList = (path, emitter) => {
let standardFolders = [];
fs.readdir(path, (err, files) => {
if (files) {
files.map((file) => {
winattr.get(path + file, function (err, attrs) {
if (err == null && attrs.directory && (!attrs.hidden && !attrs.system)) {
standardFolders.push(file)
}
});
});
} else {
standardFolders = null;
}
emitter('getFileList', standardFolders);
});
};
第二部分我的代码有什么问题?
winattr.get(filepath,callback)
是异步的,所以想象一下你的代码 "starts" file.map()
行然后立即跳到 emitter('getFileList',standardFolders)
--- 其中 standardFolders
是空的因为它还没有完成!
您可以使用像 async.io 这样的库来处理您的回调函数,或者您可以使用计数器并自行跟踪所有回调(针对每个文件)何时完成。
示例:
// an asynchronous function because setTimeout
function processor(v,cb){
let delay = Math.random()*2000+500;
console.log('delay',delay);
setTimeout(function(){
console.log('val',v);
cb(null,v);
},delay);
}
const main = function(){
const list = ['a','b','c','d'];
let processed = [];
let count = 0;
console.log('starting');
list.map(function(v,i,a){
console.log('calling processor');
processor(v,function(err,value){
processed.push(v);
count+=1;
console.log('count',count);
if(count>=list.length){
// all are finished, continue on here.
console.log('done');
}
})
})
console.log('not done yet!');
};
main();
同样,对于您的代码:
const getFilesList = (path, emitter) => {
let standardFolders = [];
fs.readdir(path, (err, files) => {
if (files) {
let count = 0;
files.map((file) => {
winattr.get(path + file, function (err, attrs) {
if (err == null && attrs.directory && (!attrs.hidden && !attrs.system)) {
standardFolders.push(file)
}
count+=1;
if(count>=files.length){
// finally done
emitter('getFileList', standardFolders);
}
});
});
} else {
standardFolders = null;
emitter('getFileList', standardFolders);
}
});
};
正如在另一个答案中已经说过的那样,winattr.get
是异步的,因此循环在 winattr.get
的任何回调被调用之前完成。
您可以使用 async
/await
和 primitify
将您的代码转换为看起来几乎像同步版本的代码,并且您可以完全摆脱回调或计数器
const {promisify} = require('util')
const readdir = promisify(require('fs').readdir)
const winattrget = promisify(require('winattr').get)
const getFilesList = async (path, emitter) => {
let standardFolders = [];
try {
let files = await readdir(path);
for (let file of files) {
try {
let attrs = await winattrget(path + file)
if (attrs.directory && (!attrs.hidden && !attrs.system)) {
standardFolders.push(file)
}
} catch (err) {
// do nothing if an error occurs
}
}
} catch (err) {
standardFolders = null;
}
emitter('getFileList', standardFolders);
};
附加说明:在您的代码中您编写了 files.map
,但是映射用于转换给定数组的值并将它们存储在新数组中,而这在您当前的代码中并未完成,所以在给定的情况下你应该使用 forEach
循环而不是 map
.
有一个函数,我用它来读取目录中的所有文件,然后将带有发射器的对象发送到客户端。
我的代码运行良好,
const getFilesList = (path, emitter) => {
fs.readdir(path, (err, files) => {
emitter('getFileList', files);
});
};
但是当我想用这段代码过滤隐藏文件时,'standardFolders' 将在发射器中发送空文件。
const getFilesList = (path, emitter) => {
let standardFolders = [];
fs.readdir(path, (err, files) => {
if (files) {
files.map((file) => {
winattr.get(path + file, function (err, attrs) {
if (err == null && attrs.directory && (!attrs.hidden && !attrs.system)) {
standardFolders.push(file)
}
});
});
} else {
standardFolders = null;
}
emitter('getFileList', standardFolders);
});
};
第二部分我的代码有什么问题?
winattr.get(filepath,callback)
是异步的,所以想象一下你的代码 "starts" file.map()
行然后立即跳到 emitter('getFileList',standardFolders)
--- 其中 standardFolders
是空的因为它还没有完成!
您可以使用像 async.io 这样的库来处理您的回调函数,或者您可以使用计数器并自行跟踪所有回调(针对每个文件)何时完成。
示例:
// an asynchronous function because setTimeout
function processor(v,cb){
let delay = Math.random()*2000+500;
console.log('delay',delay);
setTimeout(function(){
console.log('val',v);
cb(null,v);
},delay);
}
const main = function(){
const list = ['a','b','c','d'];
let processed = [];
let count = 0;
console.log('starting');
list.map(function(v,i,a){
console.log('calling processor');
processor(v,function(err,value){
processed.push(v);
count+=1;
console.log('count',count);
if(count>=list.length){
// all are finished, continue on here.
console.log('done');
}
})
})
console.log('not done yet!');
};
main();
同样,对于您的代码:
const getFilesList = (path, emitter) => {
let standardFolders = [];
fs.readdir(path, (err, files) => {
if (files) {
let count = 0;
files.map((file) => {
winattr.get(path + file, function (err, attrs) {
if (err == null && attrs.directory && (!attrs.hidden && !attrs.system)) {
standardFolders.push(file)
}
count+=1;
if(count>=files.length){
// finally done
emitter('getFileList', standardFolders);
}
});
});
} else {
standardFolders = null;
emitter('getFileList', standardFolders);
}
});
};
正如在另一个答案中已经说过的那样,winattr.get
是异步的,因此循环在 winattr.get
的任何回调被调用之前完成。
您可以使用 async
/await
和 primitify
将您的代码转换为看起来几乎像同步版本的代码,并且您可以完全摆脱回调或计数器
const {promisify} = require('util')
const readdir = promisify(require('fs').readdir)
const winattrget = promisify(require('winattr').get)
const getFilesList = async (path, emitter) => {
let standardFolders = [];
try {
let files = await readdir(path);
for (let file of files) {
try {
let attrs = await winattrget(path + file)
if (attrs.directory && (!attrs.hidden && !attrs.system)) {
standardFolders.push(file)
}
} catch (err) {
// do nothing if an error occurs
}
}
} catch (err) {
standardFolders = null;
}
emitter('getFileList', standardFolders);
};
附加说明:在您的代码中您编写了 files.map
,但是映射用于转换给定数组的值并将它们存储在新数组中,而这在您当前的代码中并未完成,所以在给定的情况下你应该使用 forEach
循环而不是 map
.