I was looking for a way to do nested function calls nicely, to avoid something like:
var result = function1(function2(function3()));
Or something like:
var result = function3();
result = function2(result);
result = function1(result);
Something like piping from Unix would be nice:
var result = function3() | function2() | function1();
Source: https://www.npmjs.com/package/babel-plugin-operator-overload
Of course |
is the bitwise or operation, this example is abstract.
Does anybody know of any way to achieve an effect like this using ES5, ES6, or ES7, without transpiling?
Thank you T.J Crowder, torazaburo, and Bergi, you all added unique, useful, and interesting information in your answers.
I took your question initially to be doing this without any helper function, but your subsequent comment suggests that that's not the case. Skip down if helper functions are in scope.
Without adding any helper functions, you can use ES6 promises:
Promise.resolve()
.then(function3)
.then(function2)
.then(function1)
.then(result => {
console.log("result is " + result);
});
It's not prettier than
var result = function1(function2(function3()));
...but at least the functions being called are listed in the order they're called, and promises are very quite flexible in multiple ways.
E.g: 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);
});
Output:
function3 called function2 called with result3 function1 called with result2 result is result1
Re your comment:
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); });
I know pipe is a thing in fs libraries, so the function name isn't exactly great. Aside from that, is there anything glaringly wrong with this?
If you want to throw a helper function at this, there's no need at all for eval
. For non-promise-ified functions, just do:
function pipe(first, ...more) {
return more.reduce((r, f) => f(r), first());
}
and
let result = pipe(function3, function2, function1);
If you want to do this with promise-ified functions or a mix, then:
function pipe(...functions) {
return functions.reduce((p, f) => p.then(f), Promise.resolve());
}
then you can call it the way you showed:
pipe(function3, function2, function1, result => {
// ...
});
...but doing so ignores errors. Since pipe
returns the last promise, you can use all the promise goodness
pipe(function3, function2, function1, result => {
// ...
}).catch(failure => {
// ...
});
or
pipe(function3, function2, function1)
.then(result => {
// ...
})
.catch(failure => {
// ...
});
Here's a complete example mixing simple functions and functions that return promises: 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);
});
Output:
function3 called function2 called with result3 function1 called with result2 Final result is result1