使用可读的函数名称展平promise链

时间:2017-05-01 17:41:16

标签: javascript promise bluebird

我看到Handling multiple catches in promise chain中的promise实现产生了一个非常易读的链

return validateInput
                 .then(checkLoginPermission)
                 .then(checkDisableUser)
                 .then(changePassword);

但是,为了做到这一点,每个函数都需要返回一个值而不是一个Promise?因为Promise可以解析为值或Promise,所以这不是问题。我的目标是让每个函数都具有可读的清晰逻辑。

尝试展开嵌套的promise函数

时会出现问题
return validateInput
       .then(function(resultA) {
            return checkLoginPermission
               .then (function(resultB) {
                   // Do something with resultA
               })
       });

想象一下原始实现涉及访问先前承诺的值。使用嵌套承诺,很容易实现。但是对于扁平链,我需要像这样分解每个函数

function validateInput = function (resultA ) {
        return Promise.resolve({resultA : resultA, resultB : 
}
function checkLoginPermission = function (mix ) {
        let resultA = mix.resultA;
        let resultB = mix.resultB
        //Do something with resultA
        ... 
}

当链中的最后一个函数从一开始就依赖某些东西时,情况会更糟。这意味着即使没有使用,该值也必须从链的开头传递下来。

所以我不小心踩到某种可能影响性能的反模式?如果没有这些麻烦,我怎样才能获得良好的可读性?

4 个答案:

答案 0 :(得分:4)

这实际上是asyncawait进来的地方。当您需要跨多个异步调用/承诺的结果时,它会很好。如果你可以使用它,我会说尝试一下。

async function foo () {
    const input = await validateInput()
    const hasPermission = await checkLoginPermission(input)
    const result = await checkDisableUser(hasPermission)
    return await changePassword(result)
}

只需将变量传递到需要的函数中即可。只是在那里展示一个例子。我也不确定你是如何设置validateInput的,我认为你需要将await放在函数调用本身的前面。

如果你不能使用async / await,我通常会使用你的第二个代码片段,或者在ontop上定义更高范围的变量:

let resultA
return validateInput
   .then(function(result) {
        resultA = result
        return checkLoginPermission
           .then (function(resultB) {
               // Do something with resultA
           })
   });

答案 1 :(得分:1)

Promise是与函数式编程相关的模式,从一个函数到另一个函数的直接传递数据是基本的(它被称为compose,这里的例子是:http://scott.sauyet.com/Javascript/Talk/Compose/2013-05-22/)。所以它绝不是反模式。

我无法看到这种模式中的任何问题。您可以将所需的任何数据传递给下一个Promises,并在嵌套的Promises中获取所需的数据。它透明而清晰:

function validateInput() {
    return Promise.resolve({resultA: 1});
}

function checkLoginPermission(result) {
    return new Promise(function(resolve, reject) {
        // ...
        // code
        // ...
        result.resultB = 2;
        return resolve(result);
    });
}

function checkDisableUser(result) {
    return new Promise(function(resolve, reject) {
        // grab some data from previous function
        let resultB = result.resultB;
        // ...
        // code
        // ...
        result.resultC = 3;
        return resolve(result);
    });
}

function changePassword(result) {
    return new Promise(function(resolve, reject) {
        // grab some data from previous functions
        let resultB = result.resultB;
        let resultC = result.resultC;
        // ...
        // code
        // ...
        result.resultD = resultB * resultC;
        return resolve(result);
    });
}

validateInput()
    .then(checkLoginPermission)
    .then(checkDisableUser)
    .then(changePassword);

此外,您可以在Promises之前声明的某个变量中收集数据,因此您不必传递结果。但它会破坏Promises的功能性。

答案 2 :(得分:0)

内部.then(/* ... */)回调可以返回原始值或解析为某个值的Promise。如果它是另一个承诺,则下一个承诺。然后才开始直到内部承诺得到解决。从本质上讲,Promises总是解析为非承诺类型。如果您解决或返回另一个Promise,它将自动解包。

答案 3 :(得分:0)

我想使用ramda.js#pipeP()提出解决方案。

这个功能的好处在于它能够顺序解决承诺。

我们可以使用pipeP()重写您的示例:

import pipeP from 'ramda/src/pipeP'

pipeP([
  checkLoginPermission,
  checkDisableUser,
  changePassword
])(initialValue)
.then(responseChangePassword => { ... })

先前承诺的结果将传递给下一个承诺。