使用承诺重构代码以读取文件并将它们转换为 json
Posted
技术标签:
【中文标题】使用承诺重构代码以读取文件并将它们转换为 json【英文标题】:Refactor code with promises to read files and convert them to json 【发布时间】:2020-06-06 21:16:27 【问题描述】:我正在尝试执行以下操作:读取目录的内容以查找所有 .xml 文件(我使用的是glob,但我想使用 fs 中的 fs.readdir 之类的东西),然后我想使用 fs.readFile 读取每个文件,然后我想将 xml 文件转换为 JSON 对象。为此,我使用xml2json。
一旦我有了 json 对象,我想迭代它们中的每一个以从中获取一个属性并将其推送到数组中。最终,所有代码都包装在一个记录数组内容的函数中(一旦完成)。这段代码目前运行良好,但我正在进入著名的回调地狱。
const fs = require('fs');
const glob = require('glob');
const parser = require('xml2json');
let connectors = []
function getNames()
glob(__dirname + '/configs/*.xml', , (err, files) =>
for (let j=0; j < files.length; j++)
fs.readFile( files[j], function(err, data)
try
let json = parser.toJson(data, object: true, alternateTextNode:true, sanitize:true)
for (let i=0; i< json.properties.length; i++)
connectors.push(json.properties[i].name)
if (connectors.length === files.length)return console.log(connectors)
catch(e)
console.log(e)
);
)
getNames()
但是,我想转向一个更干净、更优雅的解决方案(使用 Promise)。我一直在阅读社区,并在here 或here 的一些类似帖子中找到了一些想法。
我想听听您对我应该如何处理这种情况的意见。我应该改用 readFile 的同步版本吗?我应该使用promisifyAll 重构我的代码并在任何地方使用promise 吗?如果是这样,您能否详细说明我的代码应该是什么样的?
我还了解到从节点 v10.0.0 开始有一个 promises based version of fs。我应该选择那个选项吗?如果是这样,我应该如何处理 parser.toJson() 部分。我还看到还有另一个基于 Promise 的版本,称为 xml-to-json-promise。
非常感谢您对此的见解,因为当涉及多个异步操作和循环时,我对 Promise 不是很熟悉,因此我最终会为这种情况提供肮脏的解决方案。
问候, J
【问题讨论】:
connectors.length === files.length
背后的逻辑是什么?一个和另一个有什么关系?
@trincot 这是一种了解连接器数组何时已完全填充的方法。当要填充的数组长度(连接器)等于文件数时。然后我返回控制台日志,并在调用函数时填充数组。
我不明白:每个文件可以有多个属性,那么文件数与属性总数有何关系?另外,请注意return
不会中断处理,仍然可以执行更多readFile
回调。
这个想法是文件是一个由 x 个文件名组成的数组,旨在由 fs.ReadFile 读取。该 if 语句仅用于检查连接器数组是否已完全填充(它的元素数量与文件数量相同)。但现在我再想一想,你是对的,因为每个文件可以有多个属性。我怎么知道数组何时被填充?感谢您指出这一点
【参考方案1】:
我确实建议你使用glob
和fs
的promise 版本,然后使用async
、await
和Promise.all
来完成这一切。
注意:我看不到有关connectors.length === files.length
检查的逻辑,因为理论上连接器(属性)的数量可能大于文件的数量。我假设你想收集他们的所有,不管他们的数量。
以下是代码的外观(未经测试):
const fs = require('fs').promises; // Promise-version (node 10+)
const glob = require('glob-promise'); // Promise-version
const parser = require('xml2json');
async function getNames()
let files = await glob(__dirname + '/configs/*.xml');
let promises = files.map(fileName => fs.readFile(fileName).then(data =>
parser.toJson(data, object: true, alternateTextNode:true, sanitize:true)
.properties.map(prop => prop.name)
));
return (await Promise.all(promises)).flat();
getNames().then(connectors =>
// rest of your processing that needs access to connectors...
);
正如您在 cmets 中所写的那样,您在访问 properties.map
时遇到问题,执行一些验证,并跳过不存在 properties
的情况:
const fs = require('fs').promises; // Promise-version (node 10+)
const glob = require('glob-promise'); // Promise-version
const parser = require('xml2json');
async function getNames()
let files = await glob(__dirname + '/configs/*.xml');
let promises = files.map(fileName => fs.readFile(fileName).then(data =>
(parser.toJson(data, object: true, alternateTextNode:true, sanitize:true)
.properties || []).map(prop => prop.name)
));
return (await Promise.all(promises)).flat();
getNames().then(connectors =>
// rest of your processing that needs access to connectors...
);
【讨论】:
这是什么.flat()
?哦……这是 Array.smoosh!不错!
它创建一个二维数组(连接器块)的一维数组。如果没有支持,也可以通过[].concat(...await Promise.all(promises))
来完成。
@trincot 这个想法很棒,而且似乎是前进的方向。但是,我遇到一个错误,提示 (node:41475) UnhandledPromiseRejectionWarning: TypeError: parser.toJson(...).properties.map is not a function。但是,如果我将 prop=> prop.name 替换为 prop=> console.log(prop.name) 它会记录我想要的所有值。可能是什么问题?
这意味着您遇到toJson()
返回一个没有properties
属性的对象的情况。所以你可能应该首先将toJson()
的结果保存在一个变量中,然后检查它是否有properties
,然后决定在任何一种情况下你想要发生什么......
查看补充以了解如何处理丢失的properties
。以上是关于使用承诺重构代码以读取文件并将它们转换为 json的主要内容,如果未能解决你的问题,请参考以下文章