escape-from-callback-mountain icon indicating copy to clipboard operation
escape-from-callback-mountain copied to clipboard

Show concurrency limits & timeout example

Open justsml opened this issue 7 years ago • 0 comments

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))

justsml avatar May 11 '17 06:05 justsml