Proper way of using loops in Promises

2019-02-25 00:32发布

问题:

According to this link (Rookie mistake #2) I should not use loops within Promises, but instead Promise.all(iterable).

Does this really apply to all loops? Promise.all(iterable) takes an array of size n. If I'd use Promise.all(iterable), then I'd get as a result (i.e. iterable_A) an array of the size n.

What if I want to iterate through iterable and only want to put certain elements that satisfy my condition to another iterable (e.g. iterable_B) and want to return the iterable_B instead of iterable_A? Should I use Promise.all() too?

回答1:

I should not use loops within Promises

No, rather the other way round: You should not use promises within loops.

Of course that's too generic as well. Sometimes you just need a loop structure. What you must not do is forget to collect the promises that are created in the loop body in some iterable that you can pass to Promise.all, to await all the asynchronous things started in that loop.

The map method as suggested in the article naturally does that, you just have to return a promise from the callback (as always). Using for/while/.forEach makes it a bit harder as you have to manually push the promises in some array (which is not only ugly but also errorprone).

However, if you are not dealing with asynchronous tasks inside your loop, you can do whatever you want. For example, both

Promise.all(values.filter(syncPredicate).map(asyncFn))

and

Promise.all(promises).then((values) => values.filter(syncPredicate))

are totally fine. It does become a bit more complicated when you have an asynchronous filter predicate, I'd recommend to look out for a promise utility library in that case.

Also you will have to realise that asynchronous tasks started from within a synchronous loop construct will run in parallel. If you intend to run them sequentially (await each iteration), you should try to formulate the loop using a recursive structure.