escape-from-callback-mountain
escape-from-callback-mountain copied to clipboard
Show concurrency limits & timeout example
To prevent prematurely creating Promises and exhausting resurces, we'll start our Promise chain by simply passing our parent function's params (ids
array) into Bluebird .map
handler calling our api method - now with sane limits:
// In order to avoid overwhelming `api.getById` - for example lets assume we are limited to 4 concurrent connections we can easily add or remove this kind of real-world requirement
const getUsersById = (ids = []) => Promise.resolve(ids)
.map(api.getById, {concurrency: 4})
// Call getUsers, but attach logic to extract names out
const userNames = getUsersById([1, 2, 3])
.all().spread((users) => users.map(u => u.name))
userNames.then(console.log)) // > ['Emily', 'Mel', 'Paris']
// Or reuse Promise w/ built-in memoization.
// +1 for caching lookups + disposing of objects in RAM using JS scope & garbage collection.
const getUserNamesAndActivity = (userIds) => {
const users = getUsersById(userIds)
const userActivity = users.map(({username}) => api.getTopActivity({username}), {concurrency: 4}) // throttle net-bound API calls
const userNames = users.map(({name}) => name) // no I/O or net-bound call: don't throttle
return Promise.props({userNames, userActivity});
}
This "requirement" has a funny way of coming up really late in the game, maybe even in production or perhaps after a massive spike in traffic.
Avoid issues caused by "launching" too many Promises at once: simply 'start' with an array of primitive values or with something like: Promise.resolve(Array.from({length: 5000}))
Then you just need to call .map
and specify the concurrency
limit, like so:
Promise.resolve(Array.from({length: 5000}))
.map((x, idx) => api.activityByIndex(idx))