0

Given the code below, how can I pass id to the applySaveAsync function?

   var then = _.curry(function (f, thenable) {
        return thenable.then(f);
    });

    var validateAsync = _.flow(
        function () { return _(someCondition).showError(ERROR_01).value(); },  
        then(function () { return _(anotherCondition).showError(ERROR_02).value(); }) 
    );

    var save = _.flow(
        validateAsync,
        then(applySaveAsync),
        then(saveCompleted)
    );

    function applySaveAsync(id) {
        // Saving...
    }

    save(22); // Calling save function with some id.

I can get the id on the validateAsync function, but I cannot return it back since validateAsync should return a promise.

Any way to achieve that?

Anas
  • 5,622
  • 5
  • 39
  • 71
  • I don' t think lodash curry & flow are promise aware. And I'm not sure what they add to the table even if they were. If you had save as a normal function the id would be captured (job done). – Keith Oct 01 '16 at 07:21
  • What do you mean "you cannot return it back"? As far as I can see your `validateAsync` function does return a promise that fulfills with the id. Where is your problem? Maybe show us more of your code. – Bergi Oct 01 '16 at 10:10
  • @Bergi I have included the code of `validateAsync `. Thanks for your help – Anas Oct 01 '16 at 16:20
  • @Keith I'm more interested in writing this code using functional programming, I know it can easily be solved using a normal function, or even exposing `id` as a global variable. – Anas Oct 01 '16 at 19:54

2 Answers2

3

The simplest choice would be not to use _.flow for the definition of validateAsync.
Since validateAsync does not take parameters nor has a result, you should just change the definition of save to not use _.flow:

function save(id) {
    return validateAsync()
    .then(function(){ return applySaveAsync(id) })
    .then(saveCompleted)
}

We could also change validateAsync to pass through the id:

function validateAsync(id) {
    return _(someCondition).showError(ERROR_01).value()  
    .then(function () { return _(anotherCondition).showError(ERROR_02).value(); })
    .then(_.constant(id));
}

and even do that while still using _.flow

var validateAsync = _.flow(
    function(id) { return _(someCondition).showError(ERROR_01).value().then(_.constant(id)); },  
    then(function(id) { return _(anotherCondition).showError(ERROR_02).value().then(_.constant(id)); }) 
);

but I would advise against that since validateAsync is not supposed to be a function that does takes parameters.

Let's write a wrapper function for such instead to let us do the pass-around in a functional way:

function pass(fn) {
    return function(id) {
        return fn().then(function() {
            return id;
        });
    }
}

(if you prefer, you can try to compose that from then, _.constant and more)
so that one can write

var save = _.flow(
    wrap(validateAsync),
    then(applySaveAsync),
    then(saveCompleted)
);
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • You think there is no way to pass the `id` using composition? (I'm still learning fp). – Anas Oct 01 '16 at 19:56
  • @Anas see the edit. If you want to use composition, you need some way to pass the `id` through or around `validateAsync`. A large problem of `validateAsync` is that it is not pure (called only for side effects) and does not take an argument, which makes it compose very bad – Bergi Oct 01 '16 at 20:36
  • Thanks a lot for your explanation. – Anas Oct 01 '16 at 21:12
  • This is good. I made my 'then' function like this ``` const then = (fn) => { return (input: Promise) => input.then(fn); }; ``` – Joe Aug 16 '22 at 16:08
0

I found this package useful for you. In Async cases, you can use this package.

Although flow is one of the best implementations for declarative programming, it doesn't support modern JS programming style.

import { Conductor } from '@puzzleio/conductor';

const conductor = Conductor.createDefault();

const myAsyncWorkflow = conductor
  .add(validateAsync)
  .if({
    check: item => item.isValid === true,
    handler: item => console.log('Item is valid')
  },
  {
    // else block
    handler: item => console.log('Validation failed')
  });

myAsyncWorkflow.run(obj)
  .then(() => console.log('Successfully validated'))
  .catch(console.error);