最近碰到的一个问题,怎么去实现类似于 pipeline(add,reduce,multi)(5) 这样一个函数。这个函数就是一个管道函数,前一个函数结果作为后一个函数参数。管道函数的好处是不用写很多的嵌套,程序更加通俗易懂。
分析
实现管道函数不难,就是循环函数列表,然后执行,将执行结果保存作为下一次执行时的参数。
这里有两种方法,一种是用 while / for 循环手动实现;另外一种就是用到了数组方法 reduce。
实现
普通方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>管道函数 pipeline</title>
</head>
<body>
<script>
// 用了ES6的结构运算符,可能部分浏览器不支持
function pipeline(...funs){
var value;
return function(val){
value = val;
while(funs.length){
value = funs.shift()(value);
}
return value;
}
}
function add5(a){
return a + 5;
}
function mult3(a){
return a * 3;
}
function print(a){
alert(a);
}
pipeline(add5, mult3, add5, add5, print)(6);
</script>
</body>
</html>
Reduce 方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>管道函数 pipeline</title>
</head>
<body>
<script>
function pipeline(...funs){
return function(val){
return funs.reduce(function(prev, fn){
return fn(prev);
}, val)
}
}
function add5(a){
return a + 5;
}
function mult3(a){
return a * 3;
}
function print(a){
alert(a);
}
pipeline(add5, mult3, add5, add5, print)(6);
</script>
</body>
</html>
看实现很简单吧,但是呢,这些管道连接的函数都是同步函数,都是能直接得到结果的,如果其中出现了异步函数,这个管道函数就不能满足要求了。所以这里我们需要一个异步管道解决方案。
关于异步解决方案目前有两种:一种就是回调函数;另外一种就是 ES6 新增加的 Promise 对象。这里的异步方案我们就用 Promise,毕竟用的爽。
管道函数的异步方式
支持异步的管道函数整体思路上和上面一样,只不过对每次的运算结果要进行判断,如果不是一个异步方案,那么就继续参与循环迭代;如果是一个异步方案,那么我们就要跳出循环,用异步的方式执行一个新的管道函数。
实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>管道函数 pipeline</title>
</head>
<body>
<script>
function pipeline(...funs){
var value;
return function(val){
value = val;
while(funs.length){
value = funs.shift()(value);
// 判断返回值是否是异步方案
if(value instanceof Promise){
return value.then(function(val){
pipeline(...funs)(val);
})
}
}
return value;
}
}
function add5(a){
return new Promise(function(resolve, reject){
// 模拟异步请求
setTimeout(function(){
resolve(a + 5);
}, 1000)
})
}
function mult3(a){
return a * 3;
}
function print(a){
alert(a);
}
pipeline(add5, mult3, add5, add5, print)(6);
</script>
</body>
</html>