That being said my biggest hurdles are always combining blocking with async code. That usually bloats my code. Examples are blocking LDAP search that needs not block the event loop.
0 - https://vertx.io/docs/apidocs/io/vertx/core/Future.html
1 - https://vertx.io/docs/apidocs/io/vertx/core/Promise.html
The most abstract I'm usually willing to get is using River [5] instead of array's map/filter/reduce.
There seems to be an absolute obsession over these libraries now though. I assume it's fueled by every frontend library pushing functional patterns. See Effect [1] for example, it's insane how much is piled on.
[2] type Fun<A, B> = (a: A) => B export const pipe = <A, B>(f: Fun<A, B>) => { return { to: <C>(g: Fun<B, C>) => pipe((arg: A) => g(f(arg))), start: () => f, }; };
[4] class TaskQueue<U = any> { private pending: Promise<any> = Promise.resolve(); add = <T,>(task: () => Promise<false extends true & U ? T : U>) => (this.pending = this.pending.then(task).catch(task)); }