Flattening a promise with a friendly function name

I've seen the implementation of promises in Handling multiple catches in a promise chain that creates a very readable chain

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

      

However, for this to work, each function must return a value instead of a Promise? Since a Promise can resolve either a value or a promise, so this is not a problem. My goal is to turn each function into comprehensible understandable logic as such.

The problem occurs when trying to deploy a nested promise function

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

      

Imagine that the initial implementation assumes access to the value from the previous promise. With nested promises, this is easily achievable. But with a flat chain, I would need to break every function like this

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
        ... 
}

      

It's worse when the last function in the chain relies on something from the beginning. This means that the value must be transferred from the beginning of the chain, even if it has not been used.

So, I happen to find some kind of anti-pattern that might affect performance? How else can I achieve good readability without all these problems?

+3


source to share


4 answers


It actually happens where async

and await

. It's good when you need results for multiple asynchronous calls / promises to be in scope. If you can use that I would give it a try.

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

      

Just pass the variables to which function they should be. Just show me an example. I also didn't know a bit how you set validateInput, I think you need to put await

infront of the function call itself.



If you can't use async / await, I usually use your second code snippet or set higher scope variables ontop:

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

      

+4


source


Promises are a pattern related to functional programming, where the direct transfer of data from one function to another is basic (it is called compose

, examples here: http://scott.sauyet.com/Javascript/Talk/Compose/2013-05-22/ ) ... So this is not an anti-pattern in any way.

I don't see any problem with such a pattern. You can pass any data you want next time to Promises and capture what they need in nested Promises. It's transparent and clear:



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);

      

Also you can collect data in some variable declared before Promises so you don't have to pass the result. But this will destroy the functional nature of Promises.

+1


source


Internal callbacks .then(/* ... */)

can return either a primitive value or a promise that resolves some value. If this is another promise, then the next one. Then will not start until the inner promise is resolved. Essentially, Promises always resolve a non-promise type. If you allow or return another promise, it will automatically expand.

0


source


I would like to suggest a solution using ramda.js # pipeP () .

The nice thing about this feature is that it resolves promises consistently.

We can rewrite your example using pipeP()

:

import pipeP from 'ramda/src/pipeP'

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

      

The results of the previous promise are passed on to the next one.

0


source







All Articles