使用承诺重构代码以读取文件并将它们转换为 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】:

我确实建议你使用globfs 的promise 版本,然后使用asyncawaitPromise.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的主要内容,如果未能解决你的问题,请参考以下文章

在数据表上显示图像

如何在 Node.js 中读取文件内容并将数据转换为 JSON?

将拼花地板转换为json以进行dynamodb导入

将文件列表 (JSON) 转换为数据框

将 pcap 转换为 JSON 的最简单方法

绕过内存错误以在 Python 中读取大型 JSON 文件 [关闭]