【问题标题】:Flattening promise chain with readable function name具有可读函数名称的扁平化承诺链
【发布时间】:2017-09-29 03:18:04
【问题描述】:

我在Handling multiple catches in promise chain 中看到了 Promise 实现,它产生了一个可读性很强的链

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

然而,为了做到这一点,每个函数都需要返回一个值而不是一个 Promise?由于 Promise 可以解析为 value 或 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
        ... 
}

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

那么我是否不小心踩到了某种可能影响性能的反模式?如果没有这些麻烦,我还能如何获得良好的可读性?

【问题讨论】:

    标签: javascript promise bluebird


    【解决方案1】:

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

    这个函数的好处是它按顺序解析promise。

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

    import pipeP from 'ramda/src/pipeP'
    
    pipeP([
      checkLoginPermission,
      checkDisableUser,
      changePassword
    ])(initialValue)
    .then(responseChangePassword => { ... })
    

    前一个 promise 的结果被传递给下一个。

    【讨论】:

      【解决方案2】:

      内部.then(/* ... */) 回调可以返回原始值或解析为某个值的 Promise。如果它是另一个承诺,那么下一个 .then 在内部承诺解决之前不会开始。本质上,Promises 总是解析为非 Promise 类型。如果你解析或返回另一个 Promise,它将自动解包。

      【讨论】:

        【解决方案3】:

        Promises 是模式,与函数式编程相关,直接将数据从一个函数传递到另一个函数是基本的(称为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 之前声明,因此您不必传递结果。但它会破坏 Promise 的功能性。

        【讨论】:

        【解决方案4】:

        这实际上是 async 和 await 的用武之地。当您需要跨多个异步调用/承诺的结果在范围内时,这很好。如果你能用,我会说试试看。

        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,我通常会使用您的第二个代码 sn-p,或者在顶部定义更高范围的变量:

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

        【讨论】:

        • 嘿,谢谢。是的,我在 async await 上读到了一点。虽然我更热衷于使用产量/生成器。你有什么想法吗?我正在使用 NodeJs 作为服务器
        • 对不起,我没有。对 async/await 自己还是很陌生!
        • 最后我使用 async/await!太好了!谢谢
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-10-19
        相关资源
        最近更新 更多