Wait for promises inside Promise.all to finish bef

2019-07-26 14:09发布

问题:

I have a Promise.all that executes asynchronous functions mapped on an array input if it's not null and then resolve data to a previously defined Promise:

Promise.all((inputs || []).map(input => {
  return new Promise((resolve, reject) => {
    someAsyncFunc(input)
    .then(intermediateOutput => {
      someOtherAsyncFunc(intermediateOutput )
      .then(output => {
        return Promise.resolve(output )
      })
      .catch(reason=> {
        return Promise.reject(reason)
      })
    })
    .catch(reason => {
      return Promise.reject(reason);
    })
  })
  .then(outputs => {
    resolve(outputs)
  })
  .catch(reason => {
    reject(reason)
  })
}))

I only get empty outputs before even someAsyncFunc finishes its work. How can make Promise.all wait for the promises inside to finish their asynchronous work ?

回答1:

See jfriend's comment.

someAsyncFunc and someOtherAsyncFunc are function that properly return a promise with something like return new Promise(/*...*/);

this is useless:

.then(output => {
  return Promise.resolve(output )
})

read the Promise documentation

same

.catch(reason=> {
  return Promise.reject(reason)
})

the Promise is already rejecting, you don't need to catch and reject yourself

to make sure Promises are chainable you need to return the Promise

// ...
return new Promise((resolve, reject) => {
  if(inputs == null)
    resolve([]);
  else {
    Promise.all(inputs.map(input => {
      return someAsyncFunc(input)
        .then(someOtherAsyncFunc)
    }))
      .then(resolve)
      .catch(reject)
  }
});

note I would rather not make the arry for Promise.all inline, it adds visual clutter:

return new Promise((resolve, reject) => {
  if(inputs == null)
    resolve([]);
  else {
    const myPromises = inputs.map(input => {
      return someAsyncFunc(input)
        .then(someOtherAsyncFunc)
    });
    Promise.all(myPromises)
      .then(resolve)
      .catch(reject)
  }
});

it may still fail if you made other mistakes.



回答2:

Would not just

return Promise.all((inputs || []).map(input =>
 somePromiseFunc(input).then(someOtherPromiseFunc)
);

work ?

You're not using Promise.all right the first time since it takes an array of promises as input, and not (resolve, reject) => { ... }

Promise.all is going to be rejected as soon as one of the underlying promises fails, so you don't need to try to do something around catch(error => reject(error)

Example:

const somePromiseFunc = (input) => new Promise((resolve, reject) => {
  setTimeout(() => {
    if (input === 0) { reject(new Error('input is 0')); }
    resolve(input + 1);
  }, 1000);
});

const someOtherPromiseFunc = (intermediateOutput) => new Promise((resolve, reject) => {
  setTimeout(() => {
    if (intermediateOutput === 0) { reject(new Error('intermediateOutput is 0')); }
    resolve(intermediateOutput + 1);
  }, 1000);
});

const f = inputs => {
  const t0 = Date.now()
  return Promise.all((inputs || []).map(input => somePromiseFunc(input).then(someOtherPromiseFunc)))
    .then(res => console.log(`result: ${JSON.stringify(res)} (after ${Date.now() - t0}ms)`))
    .catch(e => console.log(`error: ${e} (after ${Date.now() - t0}ms)`));
};

f(null)
// result: [] (after 0ms)

f([1, 0])
// error: Error: input is 0 (after 1001ms)

f([1, -1])
// error: Error: intermediateOutput is 0 (after 2002ms)

f([1, 2])
// result: [3,4] (after 2002ms)