【问题标题】:Functional programming syntax with promise control flows具有承诺控制流的函数式编程语法
【发布时间】:2017-01-04 10:19:05
【问题描述】:

在更多地适应 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

“问题 1”的一个解决方案可能是让函数返回一个函数,以消除在使用主函数的任何地方都需要包装函数。比如:

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 更简单、更易读。但那是好的函数式编程吗?或者有没有更好的方法来解决这个问题?

【问题讨论】:

    标签: javascript functional-programming


    【解决方案1】:

    附加参数的一个解决方案可能是使函数 返回一个函数,以消除对包装函数的需要 使用 main 函数的所有地方。
    但那是好的函数式编程吗?或者,还有更好的方法 解决这个问题?

    这很好。它被称为currying

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

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

    不,一点也不。 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 => … ));
    

    【讨论】:

      猜你喜欢
      • 2017-12-14
      • 1970-01-01
      • 2018-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-29
      • 2019-06-10
      相关资源
      最近更新 更多