5

How is Promise.all used in a ramda pipe? I think I'm missing something silly here.

const pipeline: Function = R.pipe(
    R.map((record) => record.body),
    R.map(JSON.parse),
    R.map(asyncFunction),
    console.log
);

Returns an array of promises (the input to Promise.all)

[ Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> } ]

However if I try to wait for those promises to resolve with Promise.all, like so:

const pipeline: Function = R.pipe(
    R.map((record) => record.body),
    R.map(JSON.parse),
    R.map(asyncFunction),
    Promise.all,
    R.andThen(useTheReturn)
);

I end up with a type error stating that i'm trying to use Promise.all as a constructor type.

{
    "errorType": "TypeError",
    "errorMessage": "#<Object> is not a constructor",
    "stack": [
        "TypeError: #<Object> is not a constructor",
        "    at all (<anonymous>)",
        "    at /var/task/node_modules/ramda/src/internal/_pipe.js:3:14",
        "    at /var/task/node_modules/ramda/src/internal/_arity.js:11:19",
        "    at Runtime.exports.handler (/var/task/lambda/data-pipeline/1-call-summary-ingestion/index.js:14:19)",
        "    at Runtime.handleOnce (/var/runtime/Runtime.js:66:25)"
    ]
}
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
Mmm Donuts
  • 9,551
  • 6
  • 27
  • 49

2 Answers2

6

You'll need to bind Promise.all to Promise:

const pipeline: Function = R.pipe(
  R.map((record) => record.body),
  R.map(JSON.parse),
  R.map(asyncFunction),
  R.bind(Promise.all, Promise),
  R.andThen(useTheReturn)
);

Demo:

const pipeline = R.pipe(
  R.bind(Promise.all, Promise),
  R.andThen(R.sum)
);

const promise1 = Promise.resolve(10)
const promise2 = 20
const promise3 = new Promise((resolve) => setTimeout(resolve, 100, 30))

const result = pipeline([promise1, promise2, promise3])

result.then(console.log)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
  • 1
    Sorry for the silly question, but why does `Promise.all`'s context switch here and has to be set back to `Promise` explicitly? – Mmm Donuts Feb 28 '20 at 07:03
  • Ah ha -> https://github.com/ramda/ramda/issues/2521#issuecomment-503549521 `I'm afraid it won't. Promise.all, at least in certain environments, including Chrome and Node, is not a pure function. I haven't looked at the implementation, but I assume it contains some this references.` – Mmm Donuts Feb 28 '20 at 07:33
  • Indeed. I was just writing it down, but that's a better description of the issue than mine :) – Ori Drori Feb 28 '20 at 07:34
  • Actually, with the `R.andThen(console.log)` function, it still returns a pending promise (oddly enough). – Mmm Donuts Feb 28 '20 at 07:40
  • 1
    R.andThen returns a promise with the value produced by the callback, which is `undefined` in the case of `console.log`. – Ori Drori Feb 28 '20 at 07:43
  • Is the `then` function in `result.then(console.log)` required? I thought `andThen` base that corner here : / – Mmm Donuts Feb 28 '20 at 14:28
  • I have a feeling like `R.andThen(useTheReturn)` needs to be the last function in the pipe. – Mmm Donuts Feb 28 '20 at 14:44
  • In any case the function would return a promise, because that's how promises work. – Ori Drori Feb 28 '20 at 16:04
-1

I have written a library called rubico, which makes this simpler because you don't need to worry about binding Promise nor using Promise.all yourself.

import { pipe, map } from 'rubico'

const pipeline: Function = pipe([
  map((record) => record.body),
  map(JSON.parse),
  map(asyncFunction),
  console.log,
]);

pipe is similar to ramda's pipe in that it chains functions together, but in the case of async functions, it will resolve the returned promise before passing the value on to the next function.

map is like ramda's map, but it just works with async functions.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
richytong
  • 2,387
  • 1
  • 10
  • 21
  • 8
    I've added a disclaimer to this answer, making it clear that you are the author of the library that you're recommending. This disclosure is required, per the [Help Center](https://stackoverflow.com/help/promotion), to avoid being perceived as a spammer. (I'm also not sure why you edited to remove all the contextual explanation from this answer. That made it worse, so I rolled it back, too.) Additionally, because this appears to involve a personal OSS project, you probably want to read this as well: [*How to offer personal open-source libraries?*](https://meta.stackexchange.com/q/229085) – Cody Gray - on strike May 27 '20 at 05:43