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.
- Am I right in thinking this way?
- 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?
- Is there a better way to utilize the future pipeline while still getting errors to the global error handler?
- 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.
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:
findData
: I recommend already wrapping it right there withencaseP
- as early on as possible. So you'd get(arrayList) => encaseP (Promise.reject) (arrayList)
, which can also be written likeencaseP (Promise.reject)
(because the input argument is simply passed down as-is).total1
function can now callfindData
directly and get back a Future..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.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 ofpromise (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 seefork
(orpromise
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 thetotal1
function and forwards its output to the Response would look something like:Passing
next
as a rejection callback tofork
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.