具有可读函数名称的扁平化承诺链

Posted

技术标签:

【中文标题】具有可读函数名称的扁平化承诺链【英文标题】: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
        ... 

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

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

【问题讨论】:

【参考方案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 的功能性。

【讨论】:

但请注意explicit construction anti-pattern.... 这是一个非常巧妙的解决方案。为什么我没有想过只做一个累积的对象。【参考方案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,我通常会使用您的第二个代码 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!太好了!谢谢

以上是关于具有可读函数名称的扁平化承诺链的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode——430. 扁平化多级双向链表(Java)

leetcode中等430扁平化多级双向链表

数据结构与算法之深入解析“扁平化多级双向链表”的求解思路与算法示例

深搜+栈==实现扁平化多级双向链表

LeetCode - 430 - 扁平化多级双向链表 - Java - 细喔

LeetCode 430. 扁平化多级双向链表 / 583. 两个字符串的删除操作 / 478. 在圆内随机生成点(拒绝采样圆形面积推导)