I have a situation in my JavaScript where I'm generating a number of promises which I want to attach then
/catch
conditions on to each one to deal with the individual issues.
I'm using the RSVP Promise library which lets me use allSettled
to determine when all promises have been resolved. RSVP's allSettled
does give me an evaluation of the output of what succeeded and what failed at the end of all settled promises, but by adding the then
/catch
to each Promise I can control better what to do if a particular Promise isn't fulfilled (e.g. prompt user to retry using only those that failed, etc.).
Using my trusty linter (JSHint), I keep getting "Don't make functions within a loop.
" shouting back at me, and I really want to be able to attach the then
/catch
conditions to each Promise without this kind of lint error cropping up.
I'm curious how other people get around the JSHint error regarding this (aside from just changing the JSHint rule. I think it's a good rule!) or if someone has a better idea of how to handle my generated Promises' then
/catch
use-case.
Here's an example:
let Promise = RSVP.Promise
let people = ['Marty McFly', 'Doc', 'Robocop', 'Terminator', 'Bozo']
let sendMessagesToPeople = []
let thoseSucceeded = []
let thoseFailed = []
// Dummy Promise method to send a message to a person
function sendMessageToPerson (person, msg) {
console.log(`Sending message to ${person}...`)
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) {
console.log(`✔︎ Sent "${msg}" to ${person}`)
resolve(person, msg)
return
}
console.log(`✘ Failed sending "${msg}" to ${person}`)
reject(person, msg)
}, 1000 + (Math.random() * 2000))
})
}
// Generate the array of Promises for allSettled to process
for (let i = 0; i < people.length; i++) {
let trySendMessageToPerson = sendMessageToPerson(people[i], "Hi there!")
/* Illegal, according to JSHint */
.then(() => {
thoseSucceeded.push(people[i])
})
.catch(() => {
thoseFailed.push(people[i])
})
sendMessagesToPeople.push(trySendMessageToPerson)
}
RSVP.allSettled(sendMessagesToPeople).then(() => {
console.log(`Succeeded: ${thoseSucceeded.length}, Failed: ${thoseFailed.length}`)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/rsvp/3.3.3/rsvp.min.js"></script>
EDIT:
I was really curious as to the performance differences with for
, forEach
and map
so using the answers given by @rasmeister and @hackerrdave I devised a JSPerf test to try to see which of the loops were more performant (I also threw in a while
test for fun):
https://jsperf.com/for-foreach-map-while-loop-performance-testing
In my testing the results vary wildly and are very inconclusive, so I have no idea which loop implementation is better performance-wise, but in terms of readability I think I'd have to go for the map
option.
let Promise = RSVP.Promise
let people = ['Marty McFly', 'Doc', 'Robocop', 'Terminator', 'Bozo']
let thoseSucceeded = []
let thoseFailed = []
// Dummy Promise method to send a message to a person
function sendMessageToPerson (person, msg) {
console.log(`Sending message to ${person}...`)
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) {
console.log(`✔︎ Sent "${msg}" to ${person}`)
resolve(person, msg)
return
}
console.log(`✘ Failed sending "${msg}" to ${person}`)
reject(person, msg)
}, 1000 + (Math.random() * 2000))
})
}
let sendMessagesToPeople = people.map((person) => {
return sendMessageToPerson(person, "Hi there!")
.then(() => {
thoseSucceeded.push(person)
})
.catch(() => {
thoseFailed.push(person)
})
})
RSVP.allSettled(sendMessagesToPeople).then(() => {
console.log(`Succeeded: ${thoseSucceeded.length}, Failed: ${thoseFailed.length}`)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/rsvp/3.3.3/rsvp.min.js"></script>
This would gather up the promises into an array where you can then respond to the m all.