即使在 Node.js 中的回调函数之后,函数仍会异步运行
Function behaving asynchronously even after a callback function in Node.js
我正在尝试使用 Node.js 的 'fs' 模块制作一个文件浏览器。
我编写了以下函数,它接受一个路径并将该路径的内容 (files/folders) 存储在一个数组中,然后使用回调将其发回。
listDir: function (path, myCallback) {
var resultObj = [];
fs.readdir(path, function (err, data) {
console.log('In listDir');
if (err) {
return myCallback(err, null);
} else {
data.forEach(function (value) {
fs.lstat(path + '/' + value, function (err, stats) {
if (err) {
console.log(err);
} else {
//console.log('Size-' + stats.isFile()+'\n');
if(stats.isFile()){
//console.log('is file----\n');
/*resultObj.push({
type: 'file',
name: value,
size: stats['size']
});*/
resultObj.push(value);
console.log(resultObj+'\n');
} else if(stats.isDirectory()){
//console.log('is folder----\n');
/*resultObj.push({
type: 'folder',
name: value,
size: stats['size']
});*/
resultObj.push(value);
console.log(resultObj+'\n');
}
}
});
});
console.log('Resultant obj-' + resultObj);
return myCallback(null, resultObj);
}
});
}
对于存储在 'data' 中的每个 file/folder,我正在尝试检查它是一个文件还是一个文件夹,并根据它我想将一个对象推入我的 'resultObj' 大批。
但是,return 语句甚至在 forEach 完成之前就已执行,因此我每次都得到一个空的 resultObj。
为什么会这样?为什么即使我提供了回调函数它还是异步运行?
当我使用路径参数调用 /test 路由时,输出是这样的--
在列表目录中
结果对象-
GET /test?path=F:\dummyFolder 304 15.210 毫秒 - -
文件 1
文件 1、文件 2
文件 1、文件 2、文件夹 3
请帮我解决这个问题,我真的很难完成必须在今晚之前完成的作业。而且在作业不要使用'fs'模块的任何同步功能中也特别提到了这一点。
那么我该怎么做才能牢记该条款?
PS: 不要介意评论,我用它们来调试。
问题是您没有等待 fs.lstat()
函数为每个文件完成。您正在排队所有 fs.lstat()
呼叫,但没有等待所有呼叫完成。当它仍然是 []
.
时,您将 return 返回 resultObj
您可以通过在 data.forEach()
的回调中添加一个检查来解决此问题,以查看有多少项目调用了 fs.lstat()
的回调。
listDir: (path, myCallback) => {
let resultObj = [];
fs.readdir(path, (err, data) => {
console.log('In listDir');
if (err) {
return myCallback(err);
}
let itemsCompleted = 0
data.forEach(value => {
fs.lstat(path + '/' + value, (err, stats) => {
itemsCompleted++
if (err) {
console.log(err);
} else {
if(stats.isFile() || stats.isDirectory()){
resultObj.push(value);
console.log(resultObj+'\n');
}
}
if(itemsCompleted >= data.length) {
return myCallback(null, resultObj)
}
});
});
}
然而,上述方法仍然比 async/await
的 Promises 更复杂。 async/await
的 Promises 目前是在 Node 中处理异步控制流的首选方式。利用 util.promisify()
来承诺 fs.readdir()
和 fs.lstat()
允许我们扁平化控制流并显着提高 listDir()
的可读性。此外,通过使用 map()
而不是 forEach()
,我们可以使用 Promise.all()
为每个条目包装来自 lstatAsync()
的 returned Promise。
const {promisify} = require('util')
const fs = require('fs')
const {join} = require('path')
const readdirAsync = promisify(fs.readdir)
const lstatAsync = promisify(fs.lstat)
module.exports = {
listDir: async path => {
let resultObj = [];
let entries = await readdirAsync(path)
console.log('In listDir');
await Promise.all(entries.map(async value => {
try {
let stats = await lstatAsync(join(path, value))
if(stats.isFile() || stats.isDirectory()){
resultObj.push(value);
console.log(resultObj+'\n');
}
} catch (err) {
console.log(err)
}
}))
return resultObj
}
}
我正在尝试使用 Node.js 的 'fs' 模块制作一个文件浏览器。
我编写了以下函数,它接受一个路径并将该路径的内容 (files/folders) 存储在一个数组中,然后使用回调将其发回。
listDir: function (path, myCallback) {
var resultObj = [];
fs.readdir(path, function (err, data) {
console.log('In listDir');
if (err) {
return myCallback(err, null);
} else {
data.forEach(function (value) {
fs.lstat(path + '/' + value, function (err, stats) {
if (err) {
console.log(err);
} else {
//console.log('Size-' + stats.isFile()+'\n');
if(stats.isFile()){
//console.log('is file----\n');
/*resultObj.push({
type: 'file',
name: value,
size: stats['size']
});*/
resultObj.push(value);
console.log(resultObj+'\n');
} else if(stats.isDirectory()){
//console.log('is folder----\n');
/*resultObj.push({
type: 'folder',
name: value,
size: stats['size']
});*/
resultObj.push(value);
console.log(resultObj+'\n');
}
}
});
});
console.log('Resultant obj-' + resultObj);
return myCallback(null, resultObj);
}
});
}
对于存储在 'data' 中的每个 file/folder,我正在尝试检查它是一个文件还是一个文件夹,并根据它我想将一个对象推入我的 'resultObj' 大批。 但是,return 语句甚至在 forEach 完成之前就已执行,因此我每次都得到一个空的 resultObj。
为什么会这样?为什么即使我提供了回调函数它还是异步运行?
当我使用路径参数调用 /test 路由时,输出是这样的--
在列表目录中
结果对象-
GET /test?path=F:\dummyFolder 304 15.210 毫秒 - -
文件 1
文件 1、文件 2
文件 1、文件 2、文件夹 3
请帮我解决这个问题,我真的很难完成必须在今晚之前完成的作业。而且在作业不要使用'fs'模块的任何同步功能中也特别提到了这一点。 那么我该怎么做才能牢记该条款?
PS: 不要介意评论,我用它们来调试。
问题是您没有等待 fs.lstat()
函数为每个文件完成。您正在排队所有 fs.lstat()
呼叫,但没有等待所有呼叫完成。当它仍然是 []
.
resultObj
您可以通过在 data.forEach()
的回调中添加一个检查来解决此问题,以查看有多少项目调用了 fs.lstat()
的回调。
listDir: (path, myCallback) => {
let resultObj = [];
fs.readdir(path, (err, data) => {
console.log('In listDir');
if (err) {
return myCallback(err);
}
let itemsCompleted = 0
data.forEach(value => {
fs.lstat(path + '/' + value, (err, stats) => {
itemsCompleted++
if (err) {
console.log(err);
} else {
if(stats.isFile() || stats.isDirectory()){
resultObj.push(value);
console.log(resultObj+'\n');
}
}
if(itemsCompleted >= data.length) {
return myCallback(null, resultObj)
}
});
});
}
然而,上述方法仍然比 async/await
的 Promises 更复杂。 async/await
的 Promises 目前是在 Node 中处理异步控制流的首选方式。利用 util.promisify()
来承诺 fs.readdir()
和 fs.lstat()
允许我们扁平化控制流并显着提高 listDir()
的可读性。此外,通过使用 map()
而不是 forEach()
,我们可以使用 Promise.all()
为每个条目包装来自 lstatAsync()
的 returned Promise。
const {promisify} = require('util')
const fs = require('fs')
const {join} = require('path')
const readdirAsync = promisify(fs.readdir)
const lstatAsync = promisify(fs.lstat)
module.exports = {
listDir: async path => {
let resultObj = [];
let entries = await readdirAsync(path)
console.log('In listDir');
await Promise.all(entries.map(async value => {
try {
let stats = await lstatAsync(join(path, value))
if(stats.isFile() || stats.isDirectory()){
resultObj.push(value);
console.log(resultObj+'\n');
}
} catch (err) {
console.log(err)
}
}))
return resultObj
}
}