【问题标题】:Don't know how to use csv-writer to write a csv file row by row after API callAPI调用后不知道如何使用csv-writer逐行写入csv文件
【发布时间】:2019-06-03 11:43:13
【问题描述】:

我的用例如下。我有一个 node.js 应用程序,它使 API 调用受到外部库 limiter 的限制。然后,我以每秒 27 次调用的速率进行 API 调用,直到完成对包含我需要传递给 API 的一个参数的整个数组的循环。一旦我从 API 获得响应,我想将此数据保存在 csv 文件中。

我的想法是在从 API 获得结果后逐行编写 csv 文件。我为此使用csv-writer

根据文档

"你可以通过多次调用 writeRecords 将记录继续写入同一个文件(但需要等待上一次 writeRecords 调用的 promise 实现)。"

Promise.resolve() .then(() => csvWriter.writeRecords(records1)) .then(() => csvWriter.writeRecords(records2))

我也一直在考虑尝试让代码等到 API 调用完成并提示写入 csv 文件,但是,我不知道该怎么做。我在网上找不到任何类似的例子。所有这些似乎都是从一个对象/数组开始的。即使文档中没有建议,我也尝试为抛出的任何异常添加捕获。类似的东西。

          Promise.resolve()
    .then(() => csvWriter.writeRecords(outp))
    .catch(err => console.log(err))

这是我的代码:

var RateLimiter = require('limiter').RateLimiter;
const createCsvWriter = require('csv-writer').createArrayCsvWriter;  

 var limiter = new RateLimiter(27, 'second', true);

let final = [xxxxxxxx, xxxxxxx, ..... ]

const csvWriter = createCsvWriter({
    header: ['NAME'],
    path: 'out.csv'
});

let outp = []
final.forEach((item, index) => {

//limit the API calls according to the limiter defined above
  limiter.removeTokens(1, () => {


    ExternalAPIcall({ number: item}, (error, response) => {
       if (error) {
         console.error(error)
       } else {
         console.log(response.status_message)

        output.push(response.status_message)]

         const records = output.map(NAME => [NAME])
              csvWriter.writeRecords(records)



       }
     })


   })
 })

我希望文件是逐行写入的,然后我可以使用 append:true 来覆盖文件,如果需要,如文档中所示。但是,会抛出此错误。

(node:3540) UnhandledPromiseRejectionWarning:未处理的承诺拒绝。此错误源于在没有 catch 块的情况下抛出异步函数内部,或拒绝未使用 .catch() 处理的承诺。 (拒绝编号:86) (节点:3540)UnhandledPromiseRejectionWarning:TypeError:无法读取未定义的属性“地图” 在 ArrayCsvStringifier.getCsvLine (/Users/xxxx/Desktop/nodexcel/node_modules/csv-writer/dist/lib/csv-stringifiers/abstract.js:19:14) 在 Array.from.record (/Users/xxxx/Desktop/nodexcel/node_modules/csv-writer/dist/lib/csv-stringifiers/abstract.js:14:61) 在 Function.from (本机) 在 ArrayCsvStringifier.stringifyRecords (/Users/xxxxx/Desktop/nodexcel/node_modules/csv-writer/dist/lib/csv-stringifiers/abstract.js:14:32) 在 CsvWriter。 (/Users/xxxx/Desktop/nodexcel/node_modules/csv-writer/dist/lib/csv-writer.js:24:55) 在 Generator.next () 在 /Users/xxxx/Desktop/nodexcel/node_modules/csv-writer/dist/lib/csv-writer.js:7:71 在新的承诺 () 在 __awaiter (/Users/xxxxx/Desktop/nodexcel/node_modules/csv-writer/dist/lib/csv-writer.js:3:12) 在 CsvWriter.writeRecords (/Users/xxx/Desktop/nodexcel/node_modules/csv-writer/dist/lib/csv-writer.js:22:16) (节点:3540) UnhandledPromiseRejectionWarning:未处理的承诺拒绝。此错误源于在没有 catch 块的情况下抛出异步函数内部,或拒绝未使用 .catch() 处理的承诺。 (拒绝编号:88)

感谢您的宝贵时间。

问候, J.


已解决: 问题是 writeRecords 期待 > like Array> 并且我正在传递一个数组。 https://github.com/ryu1kn/csv-writer/blob/86a57cae5d68f91d738318aff1de3b6d55d6b718/README.md#csvwriterwriterecordsrecords,这个位解决了。

const 记录 = llenar.map(NAME => [NAME]) csvWriter.writeRecords(记录)

我已经更新了上面的代码

【问题讨论】:

    标签: node.js csv promise


    【解决方案1】:

    如果您希望逐行编写文件,那么您必须等待那些承诺,Promise.all 不会帮助您。

    但是,您可以使用 .reduce 方法使 Promise 按顺序运行。

    你可以试试下面的代码:

    var RateLimiter = require('limiter').RateLimiter;
    var createCsvWriter = require('csv-writer').createArrayCsvWriter;
    var limiter = new RateLimiter(27, 'second', true);
    
    const csvWriter = createCsvWriter({
      header: ['NAME'],
      path: './out.csv'
    });
    
    /** Mocked module - you can delete this */
    const nexmo = {
      numberInsight: {
        get: (item, cb) => {
          if (typeof cb !== 'function') {
            cb = () => {}
          }
          let msg = {
            status: 1,
            status_message: `${item.number} - have passed`
          };
          cb(null, msg);
        }
      }
    }
    
    var final = ['john', 'mike', 'elise', 'valentine']
    
    final = final.reduce((accumulator, item, index) => {
    
      return accumulator.then(() => {
    
        return new Promise(resolve => {
    
          limiter.removeTokens(1, (err, remainingReq) => {
    
            if (err) {
              resolve(Promise.resolve(err.message));
            } else {
    
              nexmo.numberInsight.get({
                level: 'basic',
                number: item
              }, (error, response) => {
    
                if (error) {
                  resolve(Promise.resolve(error.message));
                } else {
    
                  csvWriter.writeRecords([
                    [response.status_message]
                  ]).then(() => {
                    resolve(response.status_message)
                  }).catch(writerErr => {
                    resolve(writerErr.message);
                  })
    
                }
    
              })
    
            }
    
          })
    
        })
    
      })
    }, Promise.resolve());
    
    final.then(lastPromiseResponse => {
    
      console.log('final', lastPromiseResponse)
    
    }).catch(err => {
      console.log('final err', err);
    })

    【讨论】:

    • 感谢@darklightcode 的建议。它返回: UnhandledPromiseRejectionWarning: ReferenceError: csvWriter is not defined。关于您对限制器的评论。它应该是一个函数,不是吗?根据the documentation
    • @JavilondoRamires 只是为了确保您确实用上面的代码替换了final.forEach,对吧?我只改变了你的那部分代码!或者你可以对你尝试过的代码进行修改。
    • 是的,我做到了,这是一个fiddle 供参考。谢谢
    • 你没有,我已经用完整版本更新了我的答案。
    • 哦,我现在明白了。非常感谢。我已经尝试过,但它仍然没有工作“UnhandledPromiseRejectionWarning:未处理的承诺拒绝。此错误源于在没有 catch 块的情况下抛出异步函数内部,或者拒绝未使用 .catch() 处理的承诺。(拒绝 ID:2)"。 UnhandledPromiseRejectionWarning: TypeError: record.map is not a function
    【解决方案2】:

    使用以下代码参考。 在这里,我从 JSON 对象中获取了密钥作为示例。

       let someObject = yourArray[0] // JSON array
       let csvHeader = [] 
       for(let key in someObject) {
         csvHeader.push({ id: key, title: key });
       }
        const csvWriter = createCsvWriter({
            path: csvFilePath,
            header: csvHeader
        });
        return await csvWriter.writeRecords(yourArray);
    

    【讨论】:

    • 嗨,Rahul,感谢您抽出宝贵时间。不幸的是,这不起作用。这个 API 是同步工作的,因此一旦我使用 await,它就会说“await 仅在异步函数中有效”。非常感谢,不过
    • 是的,您需要将代码包装在异步方法中,因为使用了等待
    【解决方案3】:

    在调用下一个 writeRecords 调用之前,Javilondo 问题帖子中的更新代码 sn-p 和 darklightcode 的答案似乎都在等待 writeRecords 返回的承诺的履行。如果底层流无法足够快地写入文件,这可能会导致数据丢失。

    我们可以编写如下内容,以确保在上一次调用完成后调用writeRecords。由于我使用了util.promisify,因此需要 Node.js v8+。 (如果你用的是低版本的Node,就自己写吧。)

    const {RateLimiter} = require('limiter');
    const createCsvWriter = require('csv-writer').createArrayCsvWriter;
    const {promisify} = require('util');
    
    const limiter = new RateLimiter(27, 'second');
    
    const csvWriter = createCsvWriter({
        header: ['NAME'],
        path: 'out.csv'
    });
    
    const removeTokens = promisify(limiter.removeTokens.bind(limiter));
    const callExternalApi = promisify(ExternalAPIcall);
    
    final.reduce(async (promise, item) => {
        await promise;
        await removeTokens(1);
    
        const {status_message} = await callExternalApi({number: item});
        const records = [[status_message]];
        await csvWriter.writeRecords(records);
    }, Promise.resolve()).catch(e => {
        console.error(e.stack);
    });
    

    我运行这个 sn-p 并定义了 ExternalAPIcallfinal,如下所示:

    const ExternalAPIcall = (params, callback) => {
        setTimeout(() => {
            callback(null, {status_message: `success: ${params.number}`});
        }, 0);
    };
    
    const final = [1, 2, 3, 4, 5, 6, 7, 8, 9];
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-12-18
      • 2016-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多