【JS 深入】pipeline 管道函数实现及其异步处理

最近碰到的一个问题,怎么去实现类似于 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>