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.
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 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).total1
function can now call findData
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 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.