具有承诺控制流的函数式编程语法

时间:2017-01-04 10:19:06

标签: javascript functional-programming

在javascript中更多地适应函数式编程时,我想知道如何最好地(关于功能实践和简单的可读代码)解决以下问题。

我有一个返回对象替代版本的函数。

function doThis(obj) {
    return {
        ...obj,
        something: 'Changed'
    }
}

尼斯!一个简单的函数做一件事,可用于各种情况和具有其他功能的组合。

表示一个对象

// Ex 1 - One function
let changed = doThis(original);

// Ex 2 - Many functions
let changed = andMore(doThat(doThis(original)));

// Ex 3 - Many functions, that need additional arguments
let changed = andMore(doThat(doThis(original, 'foo'), 'bar'), 'foo');
// or
let changed = andMore(
    doThat(
        doThis(original, 'foo'),
        'bar'
    ),
    'foo'
);

表示对象数组

// Ex 4 - One function
let changed = originals.map(doThis);

// Ex 5 - Many functions
let changed = originals
    .map(doThis)
    .map(doThat)
    .map(andMore);

// Ex 6
let changed = originals
    .map(original => doThis(original, 'foo'))
    .map(changed => doThat(changed, 'bar'))
    .map(changed => andMore(changed, 'foo'));

但是,函数(由于某种原因)必须异步地执行它的操作,因此它们返回Promise,而是使用预期的值解析而不是像之前那样返回。

表示一个对象

// Ex 7 - One function
doThis(original)
    .then(changed => {
        // continue
    });

// Ex 8 - Many functions
doThis(original)
    .then(doThat)
    .then(andMore)
    .then(changed => {
        // continue
    });

// Ex 9 - Many functions, that need additional arguments
doThis(original, 'foo')
    .then(changed => doThat(changed, 'bar'))
    .then(changed => andMore(changed, 'foo'))
    .then(changed => {
        // continue
    });

表示对象数组

// Ex 10 - One function
Promise.all(originals => originals.map(original => doThis(original)))
    .then(changed => {
        // continue
    });

// Ex 11 - Many functions
Promise.all(originals.map(original => doThis(original)))
    .then(changed => Promise.all(changed.map(oneChanged => doThat(oneChanged)))
    .then(changed => Promise.all(changed.map(oneChanged => andMore(oneChanged)))
    .then(changed => {
        // continue
    });

// Ex 12 - Many functions, that need additional arguments
Promise.all(originals.map(original => doThis(original, 'foo')))
    .then(changed => Promise.all(changed.map(oneChanged => doThat(oneChanged, 'bar')))
    .then(changed => Promise.all(changed.map(oneChanged => andMore(oneChanged, 'foo')))
    .then(changed => {
        // continue
    });

即使运行这样一个或三个函数的简化情况,一些示例也开始变得非常丑陋。

我猜这两个主要原因是

  1. 当需要其他参数时
  2. 需要在Promise.all中包含返回promises的对象数组。
  3. “问题1”的一个解决方案可能是使函数返回一个函数,以消除在使用main函数的地方对包装函数的需要。类似的东西:

    function doThis(option) {
        return function(obj) {
            // ...
        };
    }
    
    // Ex 6 can now be simplified to
    someChain
        .map(doThis('foo'))
        .map(doThat('bar'))
        .map(andMore('foo'))
    

    但这是一个很好的函数式编程吗?或者有更好的方法来解决这个问题吗?

    “问题2”的一个解决方案可能是将函数包装在一个可重用的函数中,该函数检查传递的参数是一个对象,还是一个数组,对于后者,运行数组中每个对象的回调:

    function oneOrMany(callback) {
        return (subject) => Array.isArray(subject) ?
            subject.map(oneSubject => callback(oneSubject))
            :
            callback(subject);
    }
    
    const doThis = oneOrMany(original => {
        return {
            ...original,
            something: 'Changed'
        };
    });
    

    现在,doThis既可用于单个对象(如Ex 8),也可用于对象数组(而不是Ex 11):

    // Ex 
    doThis(originals)
        .then(doThat)
        .then(andMore)
        .then(changed => {
            // continue
        });
    

    看起来比Ex 11更简单,更易读。但这是一个很好的函数式编程吗?或者有更好的方法来解决这个问题吗?

1 个答案:

答案 0 :(得分:2)

  

其他参数的一个解决方案可能是制作函数   返回一个函数,以消除对包装函数的需要   到处使用主要功能。
  但这是一个很好的功能编程吗?或者,还有更好的方法   解决这个问题?

这很好。它被称为currying

您也可以通过编程方式执行此操作,而不是重写所有多参数函数,或者在调用位置显式执行部分应用程序,但这主要是语法差异。

  

Promise.all的一个解决方案 - 包装返回的对象数组   promises可以将函数包装在一个可重用的函数中   检查传递的参数是一个对象,一个数组和   后者,为数组中的每个对象运行回调。它可以使用   单个对象和对象数组,看起来很多   更简单,更易读   但这是一个很好的函数式编程吗?

不,一点也不。 FP是关于类型的特定,而不是根据传递的内容编写执行不同操作的开放函数。明确地这样做:

function manyParallel(callback) {
    return function(subject) {
        return Promise.all(subject.map(callback));
    };
}

manyParallel(doThis)(originals)
.then(manyParallel(doThat))
.then(manyParallel(andMore))
.then(manyParallel(changed => {
    // continue
}));
// or alternatively (but different):
manyParallel(original => original
    .then(doThis)
    .then(doThat)
    .then(andMore)
)(originals)
.then(manyParallel(changed => … ));