同步调用:完成第一个功能后调用第二个功能

时间:2016-02-08 22:05:24

标签: node.js callback promise synchronous

我最近在node.js中陈述编码,可能是一个非常简单的问题。

尝试编写XML解析器/验证器,以针对存储在Excel工作表中的值/ xpath验证xml架构和值。

现在验证功能完成后,我想调用printResult函数来打印最终结果。但是,如果我尝试在第一个函数之后立即调用该函数..它的打印变量初始值,如果在第一个函数中调用,则迭代通过excel表中存在的xpath数量并以增量打印结果。

var mocha = require('mocha');
var assert = require('chai').assert;
var fs = require('fs');
var parseString = require('xml2js').parseString;
var xpath = require('xpath');
var dom = require('xmldom').DOMParser;
var XLSX = require('xlsx');
var Excel = require("exceljs");
var should = require('chai').should();
var HashMap = require('hashmap');
var colors = require('colors');
require('/xmlValidator/dbConnect.js');

var map = new HashMap();
var elementMap = new HashMap();
var resultValue;

//console.log('hello'.green);

map.set("PASS", 0);
map.set("FAIL", 0);
map.set("INVALID_PATH", 0);

function computeResult(elementPath, result) {
  var pass = map.get("PASS");
  var fail = map.get("FAIL");
  var invalidPath = map.get("INVALID_PATH");

  elementMap.set(elementPath, result);

  if (result == "PASS") {
    pass++;
    map.set("PASS", pass);
  } else if (result == "FAIL") {
    fail++;
    map.set("FAIL", fail);
  } else {
    invalidPath++;
    map.set("INVALID_PATH", invalidPath)
  }
  printResult();
}

function printResult() {
  var pass = map.get("PASS");
  var fail = map.get("FAIL");
  var invalidPath = map.get("INVALID_PATH");
  console.log(("PASS Count :" + pass).green);
  console.log(("FAIL Count :" + fail).red);
  console.log(("Inavlid Path :" + invalidPath).yellow);
  elementMap.forEach(function(value, key) {
    if (value == "INVALID_PATH")
      console.log((key + ":" + value).yellow);
    else if (value == "FAIL")
      console.log((key + ":" + value).red);
    else
      console.log(key + ":" + value);
  });

}


var workbook = new Excel.Workbook();
workbook.xlsx.readFile('utils/' + process.argv[2])
  .then(function() {
    var worksheet = workbook.getWorksheet(1);
    worksheet.eachRow(function(row, rowNumber) {
      //console.log(rowNumber);
      var row = worksheet.getRow(rowNumber);
      var dataPath1 = row.getCell("A").value;
      var dataPath2 = row.getCell("B").value;
      var dataPath = dataPath1 + dataPath2;
      //console.log(dataPath);
      var dataValue = row.getCell("D").value;
      var flag = row.getCell("E").value;
      //console.log(flag)
      //console.log(dataValue);
      if (!flag)
        validate(dataPath, dataValue, rowNumber);

      //else console.log("NOT EXECUTED" + rowNumber)

    });


  })

function validate(dataPath, dataValue, rowNumber) {
  var fail = 0;
  fs.readFile('utils/' + process.argv[3], 'utf8', function(err, data) {
    if (err) {
      console.log("ERROR ERROR ERROR ERROR ");
      return console.log(err);
    }
    var doc = new dom().parseFromString(data);
    var subId = String(xpath.select1(dataPath, doc));
    if (subId == "undefined") {
      /*console.log('undefined caught');
				  console.log("row number :" + rowNumber);*/
      var resultValue = "INVALID_PATH";
      computeResult(dataPath, resultValue);
    } else {
      var subId = xpath.select1(dataPath, doc);
      var value = subId.lastChild.data;
      /*console.log("row number :" + rowNumber);
				  console.log("actual value: " + value);
				  console.log("expected value:" + dataValue );*/

      if (dataValue == null) {
        assert.notEqual(value, dataValue, "value not found");
        resultValue = "PASS";
        computeResult(dataPath, resultValue);
      } else {
        if (value == dataValue)
          resultValue = "PASS";
        else resultValue = "FAIL";
        computeResult(dataPath, resultValue);
      }
    }
  });
}

在上面的代码中,我想在完成执行validate函数后调用printResult()函数(workbook.xlsx.readFile)

有人可以帮我解决如何使用done()函数或进行同步调用吗?

1 个答案:

答案 0 :(得分:0)

如果fs.readFileAsync('utils/' + process.argv[3], 'utf8')可以执行一次,则validate()将完全同步,并且在验证循环后调用printResult()将是微不足道的。

在主例程中,您可以简单地汇总两个承诺......

var promise1 = workbook.xlsx.readFile();
var promise2 = fs.readFileAsync(); // requires `fs` to be promisified. 

...在开始验证循环之前。

Promise.all([promise1, promise2]).spread(/* verify here */);

也可以考虑一大堆整理,特别是:

  • 将“PASS”,“FAIL”和“INVALID_PATH”设置为常量,以避免大量重复的字符串创建,
  • 使用js plain对象代替hashmap
  • 在打印功能中从map构建elementMap
  • validate()返回其结果并在主例程中构建elementMap

这就是整个事情,尽我所能。我可能做了一些假设,但希望不会有太多不好的假设......

var mocha = require('mocha');
var assert = require('chai').assert;
var Promise = require("bluebird");
var fs = Promise.promisifyAll(require("fs")); // allow Bluebird to take the pain out of promisification.
var parseString = require('xml2js').parseString;
var xpath = require('xpath');
var dom = require('xmldom').DOMParser;
var XLSX = require('xlsx');
var Excel = require("exceljs");
var should = require('chai').should();
// var HashMap = require('hashmap');
var colors = require('colors');
require('/xmlValidator/dbConnect.js');

const PASS = "PASS";
const FAIL = "FAIL";
const INVALID_PATH = "INVALID_PATH";

function printResult(elementMap) {
    var key, result, 
        map = { PASS: 0, FAIL: 0, INVALID_PATH: 0 },
        colorNames = { PASS: 'black', FAIL: 'red', INVALID_PATH: 'yellow' };
    for(key in elementMap) {
        result = elementMap[key];
        map[(result === PASS || result === FAIL) ? result : INVALID_PATH] += 1;
        console.log((key + ": " + result)[colorNames[result] || 'black']); // presumably colors can be applied with associative syntax? If so, then the code can be very concise.
    }
    console.log(("PASS Count: " + map.PASS)[colorNames.PASS]);
    console.log(("FAIL Count: " + map.FAIL)[colorNames.FAIL]);
    console.log(("Inavlid Path: " + map.INVALID_PATH)[colorNames.INVALID_PATH]);
}

function validate(doc, dataPath, dataValue) {
    var subId = xpath.select1(dataPath, doc),
        value = subId.lastChild.data,
        result;
    if (String(subId) == "undefined") {
        result = INVALID_PATH;
    } else {
        if (dataValue === null) {
            assert.notEqual(value, dataValue, "value not found"); // not too sure what this does
            result = PASS;
        } else {
            result = (value === dataValue) ? PASS : FAIL;
        }
    }
    return result;
}

//Main routine
var workbook = new Excel.Workbook();
var promise1 = workbook.xlsx.readFile('utils/' + process.argv[2]); // from the question, workbook.xlsx.readFile() appears to return a promise.
var promise2 = fs.readFileAsync('utils/' + process.argv[3], 'utf8');

Promise.all([promise1, promise2]).spread(function(data2, data3) {
    var worksheet = workbook.getWorksheet(1),
        doc = new dom().parseFromString(data3),
        elementMap = {};
    worksheet.eachRow(function(row, rowNumber) {
        // var row = worksheet.getRow(rowNumber); // row is already a formal variable ??? 
        var dataPath, dataValue;
        if (!row.getCell('E').value)
            dataPath = row.getCell('A').value + row.getCell('B').value;
            dataValue = row.getCell('D').value;
            elementMap[dataPath] = validate(doc, dataPath, dataValue);
    });
    printResult(elementMap);
});

未经测试可能无法运行,但至少你可以搜索想法的代码。