2

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

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 实现这样的效果的任何方法,而无需转译?

编辑

谢谢 TJ Crowder、torazaburo 和 Bergi,你们都在回答中添加了独特、有用和有趣的信息。

4

3 回答 3

4

没有辅助功能

我最初认为您的问题是在没有任何辅助功能的情况下执行此操作,但您随后的评论表明情况并非如此。如果辅助函数在范围内,则跳过。

在不添加任何辅助函数的情况下,您可以使用 ES6 Promise:

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

它并不比

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

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

例如: Babel 的 REPL 上的 Live Copy

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);
});

输出:

调用函数3
用 result3 调用的 function2
使用结果 2 调用的函数 1
结果是结果1

带有辅助功能

回复您的评论:

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);

Babel 的 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 => {
        // ...
    });

这是一个混合简单函数和返回承诺的函数的完整示例: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);
    });

输出:

调用函数3
用 result3 调用的 function2
使用结果 2 调用的函数 1
最终结果是 result1
于 2015-11-08T08:09:45.520 回答
4

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

compose(function1, function2, function3) ()

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

这是一个真正简单的组合:

function compose(...fns) {
  var lastFunc = fns.pop();
  return function() { 
    return fns.reduceRight(result, fn) {
      return fn(result);
    }, lastFunc(...arguments));
  };
}
于 2015-11-08T11:10:44.353 回答
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));
}

如果您正在寻找新的语法,有一个 ES7 提案可以为该语言带来一些流水线糖。还没有确定它到底会是什么样子:

method3()::method2()::method1()
method3()->method2()->method1()
function3()->function2()->function1()
于 2015-11-08T11:40:48.397 回答