【问题标题】:nodejs - Help "promisifying" a file read with nested promisesnodejs - 帮助“承诺”使用嵌套承诺读取的文件
【发布时间】:2017-02-25 11:55:33
【问题描述】:

因此,由于 javascript 的异步行为,我最近深入研究了 Promise 及其背后的目的。虽然我“认为”我理解,但我仍然在努力解决如何承诺某些东西以返回未来值,然后执行一个新的代码块来做其他事情。我正在使用的两个主要节点模块:

  • pg-promise
  • exceljs

我想做的是读取一个文件,然后在完全读取后,迭代执行 DB 命令的每个工作表。然后在处理完所有工作表后,返回并删除我阅读的原始文件。这是我的代码。 即使有多个工作表,我也可以将所有内容都写入数据库。我没有工作的是设置它以识别所有工作表何时已完全处理,然后删除文件

workbook.csv.readFile(fileName)
            .then(function () {
                // this array I was going to use to somehow populate a true/false array.  
                // Then when done with each sheet, push a true into the array.  
                // When all elements were true could signify all the processing is done... 
                // but have no idea how to utilize this!  
                // So left it in to take up space because wtf...
                var arrWorksheetComplete = [];

                workbook.eachSheet(function (worksheet) {
                    console.log(worksheet.name);
                    db.tx(function (t) {
                        var insertStatements = [];
                        for (var i = 2; i <= worksheet._rows.length; i++) {
                            // here we create a new array from the worksheet, as we need a 0 index based array.
                            // the worksheet values actually begins at element 1.  We will splice to dump the undefined element at index 0.
                            // This will allow the batch promises to work correctly... otherwise everything will be offset by 1
                            var arrValues = Array.from(worksheet.getRow(i).values);
                            arrValues.splice(0, 1);

                            // these queries are upsert.  Inserts will occur first, however if they error on the constraint, an update will occur instead.
                            insertStatements.push(t.one('insert into rq_data' +
                                '(col1, col2, col3) ' +
                                'values($1, $2, $3) ' +
                                'ON CONFLICT ON CONSTRAINT key_constraint DO UPDATE SET ' +
                                '(prodname) = ' +
                                '($3) RETURNING autokey',
                                arrValues));
                        }
                        return t.batch(insertStatements);
                    })
                    .then(function (data) {
                        console.log('Success:', 'Inserted/Updated ' + data.length + ' records');
                    })
                    .catch(function (error) {
                        console.log('ERROR:', error.message || error);
                    });
                });
            });

我想说

.then(function(){
    // everything processed!
    removeFile(fileName)
    // this probably also wouldn't work as by now fileName is out of context?
});

但是当在 promise 中包含 promise 时,我感到非常困惑。我有 db.tx 调用,它本质上是嵌套在 .eachSheet 函数中的 promise。 请帮助愚蠢的程序员理解!在这个上已经打了好几个小时了。 :)

【问题讨论】:

  • 你需要承诺 workbook.eachSheet 并链接它。您应该将db.tx 移到eachSheet 调用之外,因为您只需要一个数据库事务。
  • Promisify 你所有的异步操作,然后当你嵌套它们时,返回一个链接它们的承诺。不要将常规异步回调与 Promise 混合使用。
  • @vitaly-t 一直在环顾四周,我很难理解如何“承诺” eachsheet 函数。有没有例子你也可以指点我?
  • 您可以通过结合使用 Promise 和 ES6 生成器来解决这个问题 - 是否有任何环境限制阻止您这样做?

标签: javascript node.js excel bluebird pg-promise


【解决方案1】:

如果我理解正确,您是在尝试链接承诺。

我建议您阅读此great article on Promises anti-pattern(请参阅“The Collection Kerfuffle”部分)

如果需要串行执行promise,本文建议使用reduce

我会把你的 sn-p 改写成:

workbook.csv.readFile(fileName).then(function () {

  processWorksheets().then(function() {
    // all worksheets processed!
  });

});

function processWorksheets() {
    var worksheets = [];

    // first, build an array of worksheet
    workbook.eachSheet(function (worksheet) {
        worksheets.push(worksheet);
    }); 

    // then chain promises using Array.reduce
    return worksheets.reduce(function(promise, item) {
        // promise is the the value previously returned in the last invocation of the callback.
        // item is a worksheet

        // when the previous promise will be resolved, call saveWorksheet on the next worksheet
        return promise.then(function(result) {
            return saveWorksheet(item, result);
        });        

    }, Promise.resolve()); // start chain with a 'fake' promise
}

// this method returns a promise
function saveWorksheet(worksheet, result) {

    return db.tx(function (t) {

      var insertStatements = [];
      for (var i = 2; i <= worksheet._rows.length; i++) {
        // here we create a new array from the worksheet, as we need a 0 index based array.
        // the worksheet values actually begins at element 1.  We will splice to dump the undefined element at index 0.
        // This will allow the batch promises to work correctly... otherwise everything will be offset by 1
        var arrValues = Array.from(worksheet.getRow(i).values);
        arrValues.splice(0, 1);

        // these queries are upsert.  Inserts will occur first, however if they error on the constraint, an update will occur instead.
        insertStatements.push(t.one('insert into rq_data' +
                                    '(col1, col2, col3) ' +
                                    'values($1, $2, $3) ' +
                                    'ON CONFLICT ON CONSTRAINT key_constraint DO UPDATE SET ' +
                                    '(prodname) = ' +
                                    '($3) RETURNING autokey',
                                    arrValues));
      }

      return t.batch(insertStatements);
    })
    // this two below can be removed...
    .then(function (data) {
       return new Promise((resolve, reject) => { 
          console.log('Success:', 'Inserted/Updated ' + data.length + ' records');
          resolve();
       });
    })
    .catch(function (error) {
        return new Promise((resolve, reject) => {
        console.log('ERROR:', error.message || error);
        reject();
      });
    });

}

不要忘记包含promise 模块:

var Promise = require('promise');

我没有测试我的代码,可能包含一些拼写错误。

【讨论】:

  • 处理数据表的关键思想之一是避免 Node.js 内存过载。不应将所有工作表都堆叠到内存中,应逐一读取和处理。
  • @vitaly-t 你是对的,我的代码都是关于链接承诺的......它可以被编辑,一种方法可能是遍历工作表文件名并在 reduce 中加载相应的文件名回调。
  • 要求发生了变化,最终只需要针对单个工作表进行处理...因此可以删除循环调用。不过,我确实适应了一些这种逻辑!谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-06
  • 2017-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多