简体   繁体   中英

Promise.all on array of objects with promises with FP

So, I'm using NodeJS and Ramda and I have an Array of objects like:

[
    {
        x: 'abc',
        y: []
    },
    {
        x: '123',
        y: [1, 2, 3]
    }
]

Then I want do use x in a request which returns a promise, resulting in this (using over and lensProp from Ramda):

[
    {
        x: Promise<String>,
        y: []
    },
    {
        x: Promise<String>,
        y: [1, 2, 3]
    }
]

Now I want to turn that last array into this:

Promise<[
    {
        x: String,
        y: []
    },
    {
        x: String,
        y: [1, 2, 3]
    }
]>

How can I achieve that in a functional way (as in functional programming, not as in something that just works =])?

The best I could think of was to get all the promises from x , use Promise.all and use then to zip the result back with the y s. But I'm not accepting that as a solution.

One option is to introduce a new helper function which behaves similar to R.traverse that is specialised to Promises and will work over a specific property of an object. Let's call this traversePropP :

// traversePropP :: (a -> Promise b) -> String -> {a|...} -> Promise {b|...}
const traversePropP = R.curry((toPromiseFn, prop, obj) =>
  toPromiseFn(obj[prop]).then(v => R.assoc(prop, v, obj)))

This effectively lets you produce a Promise from the specified property of an object, replacing the property with the eventual value resolved by the created Promise.

You can then use this new function to map over all the objects in your array, then pass the resulting array of Promises to Promise.all .

 const traversePropP = R.curry((toPromiseFn, prop, obj) => toPromiseFn(obj[prop]).then(v => R.assoc(prop, v, obj))) // example Promise-producing function that delays a value const delayP = n => x => new Promise((res, rej) => setTimeout(() => res(x), n)) const fn = R.pipe( R.map(traversePropP(delayP(500), 'x')), x => Promise.all(x) ) const data = [ { x: 'abc', y: [] }, { x: '123', y: [1, 2, 3] } ] console.log('begin') fn(data).then(x => console.log('result:', x))
 <script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

Here is a clean way to do what you want.

import { pipe, assign, map, get } from 'rubico'

const makeRequest = async x => {/* ... */}

const data = [
  { x: 'abc', y: [] },
  { x: '123', y: [1, 2, 3] },
]

map(assign({
  x: pipe([get('x'), makeRequest]),
}))(data)

final output is a promise of an array of objects with x properties overwritten with the result of the requests

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM