为什么 R.ifElse 没有返回 Promise?

Why is R.ifElse not returning a Promise?

任务

我需要制作一个应用程序来读取 CSV 文件,转换其数据,然后将其输出为另一个 CSV 文件。

输入格式如下:

13:25:37 up 19 days, 21:35,  4 users,  load average: 0.02, 0.02, 0.00
13:25:38 up 19 days, 21:35,  4 users,  load average: 0.02, 0.02, 0.00
... so on

对于那些 UNIX 爱好者,您会认出这是控制台命令的输出 uptime

我想要的输出格式如下:

 RowNum, Avg Load
 1,0.02
 2,0.02

其中第一列是CSV中的行号,第二列是load average的数字部分:0.02。

将忽略所有其他列。

问题

为了尽我所能做到这一点,我决定使用 ramda

至少可以说,这是……一个挑战。现在,我的代码有几个结构问题,但我想关注 main 函数,它不起作用。每次执行我的代码时,我都会收到错误消息:

index.js:54
    .then( () => console.log("Done!") )
     ^

TypeError: main(...).then is not a function

这令人困惑,因为在两个函数中,我传递给 R.ifElse 我 return 一个 Promise。

代码

const fs = require("fs");
const csvReader = require("csvreader");
const R = require("ramda");
const isString = require("lodash.isstring");
const { promisify } = require("util");
const argv = require("minimist")(process.argv.slice(2));
const appedFileAsync = promisify( fs.appendFile );

const createProcessData = () => {
    const stringifyArray = array => `${array.toString()}\n`;
    const write = str => fs.appendFileSync( argv.outputFile, str );

    const getAvg = R.pipe(
        R.replace("load average:", ""),
        R.trim
    );

    let elapsedTime = 1;
    const transform = list => [ elapsedTime++, getAvg ( R.nth( 3, list ) ) ];

    return R.pipe(
        transform,
        stringifyArray,
        write
    );
};

const printHelp = () => {
    console.log(`
        === MAN HELP ===
        Usage: npm start -- --inputFile input.csv --outputFile output.csv
        --inputFile: location of an input file in CSV format
        --outputFile: location of an output file to append the new information to.
        If this file does not exist, it will be created.
    `);
    return Promise.resolve();
};

const execute = () => appedFileAsync( argv.outputFile, "Time, Avg Load\n" )
    .then( ( ) => csvReader.read( argv.inputFile, createProcessData() ) );

const main = () => {
    const isInvalidFileName = R.anyPass( [ R.isNil, R.isEmpty, R.pipe ( isString, R.not ) ] );
    const hasInvalidArgs = R.either( isInvalidFileName( argv.inputFile ), isInvalidFileName( argv.outputFile ) );

    return R.ifElse(
        hasInvalidArgs,
        printHelp,
        execute
    );
};

main()
    .then( () => console.log("Done!") )
    .catch( console.error );

问题

这是ifElse的想法:

const ifElse = (predicate, consequent, alternative) => 
    (...val) => predicate(...val) ? consequent(...val) : alternative(...val);

所以

const comp = ifElse(
  (a, b) => a < b,
  (a, b) => `${a} is smaller than ${b}`,
  (a, b) => `${a} is at least as large as ${b}`
)

comp(12, 7) //=> "12 is at least as large as 7"

要点是 ifElse 的第一个参数是 函数 。但是你传递给它的结果是:

R.either( isInvalidFileName( argv.inputFile ), isInvalidFileName( argv.outputFile ) )

现在正常,either returns一个函数。但这取决于您为其提供功能。假设是,如果您不提供函数,您知道自己在做什么,并且正在使用 apmap 方法提供容器类型,因此 either 稍微更通用。但是您提供的是布尔值,例如 isInvalidFileName( argv.inputFile ) 的结果。在这一点上,行为没有明确定义。也许这应该改变,但 Ramda 的理念通常是垃圾进垃圾出。因此,无论出于何种原因,either 调用都会返回 [undefined].

这意味着您提供 [undefined] 作为 ifElse 的谓词函数。当您尝试调用它时,您应该会收到错误消息。我没有试图追查为什么您看到的那个错误被掩盖了。

至于如何使用 Ramda 解决这个问题,我不太确定从哪里开始。这与 Ramda 一贯的风格相去甚远。如果不出意外,不接受任何参数的函数在 Ramda 中是极其罕见的。我想我会用一个接受 argv.inputFileargv.outputFile 的函数来启动 Ramda 部分,无论是作为单个对象还是作为单个对象 argv.