1

I am trying to find a way to convert a Promise to a future using the fluture library to implement functional programming, transform the data using a functional pipeline, then transform back to a promise so I can utilize await/async functionality. This is for an express app and I am using global error handling and I am not seeing how to take advantage of global error handling for endpoints if I catch errors in the future process flow without converting to a promise after the future processing is complete.

  1. Am I right in thinking this way?
  2. If so, how can I use the promise utility in the code below to translate back to a promise after the encaseP and subsequent pipeline code is called?
  3. Is there a better way to utilize the future pipeline while still getting errors to the global error handler?
  4. Also, if I do convert to a promise will that also give me access to the actual value in the future if I utilize await, I assume so?

Apologies if this is a bad question, I am a newbie to functional programming and fluture and am trying to get my ducks in the proper alignment.

const arrayList = [];
arrayList.push({
    a: {
        b: 1,
        c: 2,
        d: 3
    }
},{
    a: {
        b: 2,
        c: 3,
        d: 3
    }
})

const findData = (arrayList) => Promise.reject(null)

let total1 = (list) => encaseP(findData)(list)
    .pipe(res => res)
    .pipe(map(x => x.map(y => ({
        a: {
            b: y.a.b + 1,
            c: y.a.c + 1,
            d: y.a.d + 1
        }
    }))))
    .pipe(promise (() => {console.log('do nothing')}) (console.log));


console.log(total1);
total1(arrayList);

When I run the code above I am getting the following error:

internal/modules/cjs/loader.js:992 internalBinding('errors').triggerUncaughtException( ^

TypeError: promise() expects its first argument to be a valid Future.

user1790300
  • 2,143
  • 10
  • 54
  • 123
  • 1
    What do you mean by "convert a Promise to a future"? What is the "future" that you are talking about? That isn't a built-in structure to Javascript. Are you using some library or methodology you can point us to? – jfriend00 Mar 07 '21 at 21:32
  • Usually one would just promisify non-promise-based operations by wrapping them in a promise and then use promise logic only for controlling the flow and capturing errors. It is very painful to go back and forth between promise and non-promise asynchronous operations as it makes sequencing and error flow very difficult. If you wrap all non-promise things in a promise, then you can use promise control flow and error handling for everything. – jfriend00 Mar 07 '21 at 21:35
  • I thought the utility encaseP converts a promise returning function into a future returning function? Here is the encaseP description, https://github.com/fluture-js/Fluture#encasep. I probably stated that incorrectly. – user1790300 Mar 07 '21 at 21:44
  • 1
    I'm just responding to the fact that you wrote your question assuming everyone knew exactly what library you're using and exactly what you mean by a future. Those are not standard things in Javascript so it's probably best if you add some context to the question about what library you're using and exactly what you're trying to accomplish with it that's different than what you can generally do with promises. I, for one, have never heard of `encaseP()` before and your question contains no context about where that comes from. – jfriend00 Mar 07 '21 at 21:47
  • I thought the utility encaseP converts a promise returning function into a future returning function? Here is the encaseP description, https://github.com/fluture-js/Fluture#encasep. I probably stated that incorrectly. I added the name of the library in the title, Fluture, and I will add it in the body as well. Apologies for the confusion. – user1790300 Mar 07 '21 at 21:53
  • 1
    Can you show the usage in context of the express req/res? You could go direct to response rather than inserting a promise. and I think you want [`promise`](https://github.com/fluture-js/Fluture#promise) to return a promise from a future. – Matt Mar 07 '21 at 23:00
  • If you want to utilise async/await and get back a promise in the end, there's no point using a "functional pipeline" at all. – Bergi Mar 08 '21 at 03:04
  • Since your question seems to be about express actually, can you show us that part of your code as well please? – Bergi Mar 08 '21 at 03:09

1 Answers1

2

I believe I have answered this question sufficiently in Gitter. I'll summarize my response below for future reference.


There's a few things I see in your code that I would like to address:

  1. findData: I recommend already wrapping it right there with encaseP - as early on as possible. So you'd get (arrayList) => encaseP (Promise.reject) (arrayList), which can also be written like encaseP (Promise.reject) (because the input argument is simply passed down as-is).
  2. The total1 function can now call findData directly and get back a Future.
  3. .pipe (res => res): This line actually does nothing. Here you transform res, which is a Future of an Array, into res, which is exactly the same thing. It's important to understand that pipe is just function application: https://stackoverflow.com/a/59985017/1001417
  4. .pipe (promise (() => {console.log('do nothing')}) (console.log)): When you consume a Future, the work actually starts and the consumption returns a cancellation function (or a Promise, in the case of promise (future)). You shouldn't do this inside your functions, but outside of them. I recommend reading the introduction to Fluture, especially the part about "not consuming futures". A guiding rule of thumb is that you should only see fork (or promise or any other consumption function) called at the very "edge" of your program. That's at the point where you cannot possibly pass the Future down any further to a party that'll understand it.

After removing that last line from the total1 function, so that the Future is not consumed, but returned from the function, we can consume the Future where needed. For example, an Express middleware that runs the total1 function and forwards its output to the Response would look something like:

app.get ('/total', (req, res, next) => {
  total1 (req.query.arrayList)
  .pipe (fork (next) (result => res.json ({result})))
})

Passing next as a rejection callback to fork causes Express to handle the error. This is needed for every express route that you want to use a Future inside to control async. You can use fluture-express to take away some of that boilerplate.

Avaq
  • 3,009
  • 1
  • 21
  • 25