在Javascript中嵌套许多函数调用(Unix管道)的好方法

时间:2015-11-08 07:51:52

标签: javascript ecmascript-6 ecmascript-5 ecmascript-7

我正在寻找一种很好地进行嵌套函数调用的方法,以避免类似:

var result = function1(function2(function3()));

或类似的东西:

var result = function3();
result = function2(result);
result = function1(result);

像Unix这样的管道就好了:

var result = function3() | function2() | function1();

来源:https://www.npmjs.com/package/babel-plugin-operator-overload

当然|是按位或操作,这个例子是抽象的。

有没有人知道如何使用ES5,ES6或ES7实现这样的效果而不进行转换?

修改

谢谢T.J Crowder,torazaburo和Bergi,你们在答案中都添加了独特,有用和有趣的信息。

3 个答案:

答案 0 :(得分:4)

没有辅助功能

我最初提出的问题是在没有任何帮助函数的情况下执行此操作,但您后续的评论表明情况并非如此。如果辅助函数在范围内,则省略。

不添加任何帮助函数,您可以使用ES6承诺:

Promise.resolve()
    .then(function3)
    .then(function2)
    .then(function1)
    .then(result => {
  console.log("result is " + result);
});

它不比

漂亮
var result = function1(function2(function3()));

...但至少被调用的函数按照它们被调用的顺序列出,并且promises在多种方面非常灵活。

例如:Live copy on Babel's REPL

function function1(arg) {
  console.log("function1 called with " + arg);
  return "result1";
}
function function2(arg) {
  console.log("function2 called with " + arg);
  return "result2";
}
function function3() {
  console.log("function3 called");
  return "result3";
}

Promise.resolve()
    .then(function3)
    .then(function2)
    .then(function1)
    .then(result => {
  console.log("result is " + result);
});

输出:

function3 called
function2 called with result3
function1 called with result2
result is result1

使用帮助函数

重新评论:

function pipe(){
    var str = 'Promise.resolve()';
    for(var i = 0; i < arguments.length; i++){
        str += '.then(arguments[' + i + '])'
    }
    eval(str);
}

 pipe(c, b, a, result => { console.log("result is " + result); });
     

我知道管道是fs库中的东西,所以函数名称并不是很好。除此之外,这有什么明显的错误吗?

如果你想在此处抛出辅助函数,那么eval根本不需要。对于非承诺函数,只需执行:

function pipe(first, ...more) {
  return more.reduce((r, f) => f(r), first());
}

let result = pipe(function3, function2, function1);

Live copy on Babel's REPL

如果你想使用promise-ified函数或混合函数,那么:

function pipe(...functions) {
  return functions.reduce((p, f) => p.then(f), Promise.resolve());
}

然后你可以按照你展示的方式来调用它:

pipe(function3, function2, function1, result => {
    // ...
});

...但这样做会忽略错误。由于pipe返回最后一个承诺,您可以使用所有承诺善意

pipe(function3, function2, function1, result => {
    // ...
}).catch(failure => {
    // ...
});

pipe(function3, function2, function1)
    .then(result => {
        // ...
    })
    .catch(failure => {
        // ...
    });

这是一个完整的示例,它混合了返回promise的简单函数和函数:Live copy on Babel's REPL

function pipe(...functions) {
    return functions.reduce((p, f) => p.then(f), Promise.resolve());
}
function function1(arg) {
    console.log("function1 called with " + arg);
    return "result1";
}
function function2(arg) {
    console.log("function2 called with " + arg);
    return new Promise(resolve => {
        setTimeout(() => {
            resolve("result2");
        }, 100);
    });
}
function function3() {
    console.log("function3 called");
    return "result3";
}

pipe(function3, function2, function1)
    .then(result => {
        console.log("Final result is " + result);
    })
    .catch(failure => {
        console.log("Failed with " + failure);
    });

输出:

function3 called
function2 called with result3
function1 called with result2
Final result is result1

答案 1 :(得分:4)

你只是在编写功能。使用许多库中提供的compose函数,或编写自己的函数,并将其用作:

compose(function1, function2, function3) ()

换句话说,你的“管道”操作符可以被认为是一个“逗号”,用于分隔调用中的参数。

这是一个真正简单的作品:

function compose(...fns) {
  var lastFunc = fns.pop();
  return function() { 
    return fns.reduceRight(result, fn) {
      return fn(result);
    }, lastFunc(...arguments));
  };
}

答案 2 :(得分:1)

您正在寻找的是pipe功能,基本上是众所周知的compose翻转:

var result = pipe(function3, function2, function1)();

它不是内置的(并且没有计划用于任何即将推出的ES修订版),但在Ramda等许多库中都可用;你可以自己轻松地实现它:

function pipe(g, ...fs) {
    if (!arguments.length) return x => x;
    if (!fs.length) return g;
    const f = pipe(...fs);
    return x => f(g(x));
}

如果您正在寻找新的语法,那么an ES7 proposal可能会为语言带来一些流水线糖。这是not settled yet究竟是什么样子:

method3()::method2()::method1()
method3()->method2()->method1()
function3()->function2()->function1()