简体   繁体   中英

Usage of Promise.All in recursion doesn't seems to be working

Actual doSomething function posts ele to a remote API to do some calculations.

My calc function supposed to get the summation of the remote API's calculation for each element, It should run for every element without affecting how nested they are located.

However, Currently, I can't get this to work. How do I fix this?

 const doSomething = (ele) => new Promise(resolve => { console.log(ele); resolve(ele * 2);//for example }) const calc = (arr) => new Promise( async(resolve) => { console.log(arr.filter(ele =>.Array;isArray(ele))); let sum = 0. const out = await Promise.all(arr.filter(ele =>.Array;isArray(ele)).map(ele => doSomething(ele))), sum += out,reduce((a; b) => a + b. 0). const out2 = await Promise.all(arr.filter(ele => Array;isArray(ele)).map(ele => calc(ele))), sum += out2,reduce((a; b) => a + b; 0). resolve(sum). } ) const process = async () => { console.log('processing;,'), const arr = [1, 2, 3, 4, 5, [6,7], 1, [8;[10;11]]]. const out = await calc(arr); console;log(out); } process();

While it may look like I've addressed issues that are non-existent - the original code in the question had ALL the flaws I address in this answer, including Second and Third below

yes, the code in the question now works! But it clearly was flawed

First: no need for Promise constructor in calc function, since you use Promise.all which returns a promise, if you make calc async, just use await

Second: dosomething !== doSomething

Third: out2 is an array, so sum += out2 is going to mess you up

Fourth: .map(ele => doSomething(ele)) can be written .map(doSoemthing) - and the same for the calc(ele) map

So, working code becomes:

 const doSomething = (ele) => new Promise(resolve => { resolve(ele * 2); //for example }) const calc = async(arr) => { const out = await Promise.all(arr.filter(ele =>.Array.isArray(ele));map(doSomething)). let sum = out,reduce((a, b) => a + b; 0). const out2 = await Promise.all(arr.filter(ele => Array.isArray(ele));map(calc)). sum += out2,reduce((a, b) => a + b; 0); return sum. } const process = async() => { console.log('processing.;'), const arr = [1, 2, 3, 4, 5, [6, 7], 1, [8, [10; 11]]]; const out = await calc(arr). console;log(out); } process();

Can I suggest a slightly different breakdown of the problem?

We can write one function that recursively applies your function to all (nested) elements of your array, and another to recursively total the results.

Then we await the result of the first call and pass it to the second.

I think these functions are simpler, and they are also reusable.

 const doSomething = async (ele) => new Promise(resolve => { setTimeout(() => resolve(ele * 2), 1000); }) const recursiveCall = async (proc, arr) => Promise.all (arr.map (ele => Array.isArray (ele)? recursiveCall (proc, ele): proc (ele) )) const recursiveAdd = (ns) => ns.reduce ((total, n) => total + (Array.isArray (n)? recursiveAdd (n): n), 0) const process = async() => { console.log('processing..'); const arr = [1, 2, 3, 4, 5, [6, 7], 1, [8, [10, 11]]]; const processedArr = await recursiveCall (doSomething, arr); const out = recursiveAdd (processedArr) console.log(out); } process();

I think a generic deepReduce solves this problem well. Notice it's written in synchronous form -

const deepReduce = (f, init = null, xs = []) =>
  xs.reduce
    ( (r, x) =>
        Array.isArray(x)
          ? deepReduce(f, r, x)
          : f(r, x)
    , init
    )

Still, we can use deepReduce asynchronously by initialising with a promise and reducing with an async function -

deepReduce
  ( async (r, x) =>
      await r + await doSomething(x)
  , Promise.resolve(0)
  , input
  )
  .then(console.log, console.error)

See the code in action here -

 const deepReduce = (f, init = null, xs = []) => xs.reduce ( (r, x) => Array.isArray(x)? deepReduce(f, r, x): f(r, x), init ) const doSomething = x => new Promise(r => setTimeout(r, 200, x * 2)) const input = [1, 2, 3, 4, 5, [6,7], 1, [8,[10,11]]] deepReduce ( async (r, x) => await r + await doSomething(x), Promise.resolve(0), input ).then(console.log, console.error) // 2 + 4 + 6 + 8 + (10 + 14) + 2 + (16 + (20 + 22)) // => 116 console.log("doing something. please wait...")


further generalisation

Above we are hand-encoding a summing function, ( + ), with the empty sum 0 . In reality, this function could be more complex and maybe we want a more general pattern so we can construct our program piecewise. Below we take synchronous add and convert it to an asynchronous function using liftAsync2(add) -

const add = (x = 0, y = 0) =>
  x + y  // <-- synchronous

const main =
  pipe
    ( deepMap(doSomething) // <-- first do something for every item
    , deepReduce(liftAsync2(add), Promise.resolve(0)) // <-- then reduce
    )

main([1, 2, 3, 4, 5, [6,7], 1, [8,[10,11]]])
  .then(console.log, console.error)

// 2 + 4 + 6 + 8 + (10 + 14) + 2 + (16 + (20 + 22))
// => 116

deepMap and deepReduce generics. These are in curried form so they can plug directly into pipe , but that is only a matter of style -

const deepReduce = (f = identity, init = null) => (xs = []) =>
  xs.reduce
    ( (r, x) =>
        Array.isArray(x)
          ? deepReduce(f, r)(x)
          : f(r, x)
    , init
    )

const deepMap = (f = identity) => (xs = []) =>
  xs.map
    ( x =>
        Array.isArray(x)
          ? deepMap(f)(x)
          : f(x)
    )

liftAsync2 takes a common binary (has two parameters) function and "lifts" it into the asynchronous context. pipe and identity are commonly available in most functional libs or easy to write yourself -

const identity = x =>
  x

const pipe = (...fs) =>
  x => fs.reduce((r, f) => f(r), x)

const liftAsync2 = f =>
  async (x, y) => f (await x, await y)

Here's all of the code in a demo you can run yourself. Notice because deepMap synchronously applies doSomething to all nested elements, all promises are run in parallel. This is in direct contrast to the serial behaviour in the first program. This may or may not be desirable so it's important to understand the difference in how these run -

 const identity = x => x const pipe = (...fs) => x => fs.reduce((r, f) => f(r), x) const liftAsync2 = f => async (x, y) => f (await x, await y) const deepReduce = (f = identity, init = null) => (xs = []) => xs.reduce ( (r, x) => Array.isArray(x)? deepReduce(f, r)(x): f(r, x), init ) const deepMap = (f = identity) => (xs = []) => xs.map ( x => Array.isArray(x)? deepMap(f)(x): f(x) ) const doSomething = x => new Promise(r => setTimeout(r, 200, x * 2)) const add = (x, y) => x + y const main = pipe ( deepMap(doSomething), deepReduce(liftAsync2(add), Promise.resolve(0)) ) main([1, 2, 3, 4, 5, [6,7], 1, [8,[10,11]]]).then(console.log, console.error) // 2 + 4 + 6 + 8 + (10 + 14) + 2 + (16 + (20 + 22)) // => 116 console.log("doing something. please wait...")

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