Throttle amount of promises open at a given time

前端 未结 5 1409
醉话见心
醉话见心 2020-12-03 15:28

The following Typescript performs each call to doSomething(action) one at a time. (Meaning the second item in the list does not get a call made until the first

5条回答
  •  我在风中等你
    2020-12-03 16:18

    EDIT

    Jeff Bowman has vastly improved his answer to resolve meaningful values. Feel free to view the history of this answer to understand why the resolved values are so important/useful.


    throttlep

    This solution closely mimics the native Promise.all

    How it's the same …

    • Resolves promises as quickly as possible
    • Resolves an array of values in the same order as the inputs
    • Rejects as soon as it encounters one reject

    How it's different …

    • Number parameter limits the number of simultaneously-running Promises
    • Array input accepts promise creators (thunks); not actual Promises

    // throttlep :: Number -> [(* -> Promise)]
    const throttlep = n=> Ps=>
      new Promise ((pass, fail)=> {
        // r is the number of promises, xs is final resolved value
        let r = Ps.length, xs = []
        // decrement r, save the resolved value in position i, run the next promise
        let next = i=> x=> (r--, xs[i] = x, run(Ps[n], n++))
        // if r is 0, we can resolve the final value xs, otherwise chain next
        let run = (P,i)=> r === 0 ? pass(xs) : P().then(next(i), fail)
        // initialize by running the first n promises
        Ps.slice(0,n).forEach(run)
      })
    
    // -----------------------------------------------------
    // make sure it works
    
    // delay :: (String, Number) -> (* -> Promise)
    const delay = (id, ms)=>
      new Promise (pass=> {
        console.log (`running: ${id}`)
        setTimeout(pass, ms, id)
      })
    
    // ps :: [(* -> Promise)]
    let ps = new Array(10)
    for (let i = 0; i < 10; i++) {
      ps[i] = () => delay(i, Math.random() * 3000)
    }
    
    // run a limit of 3 promises in parallel
    // the first error will reject the entire pool
    throttlep (3) (ps) .then (
      xs => console.log ('result:', xs),
      err=> console.log ('error:', err.message)
    )

    Console output

    Inputs are run in order; Resolved results are in the same order as the inputs

    running: 0
    running: 1
    running: 2
    => Promise {}
    running: 3
    running: 4
    running: 5
    running: 6
    running: 7
    running: 8
    running: 9
    result: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    

    Practical use

    Let's look at a more practical code example. This code is tasked with fetching a set of images from a server. This is how we might use throttlep to throttle the amount of simultaneous requests to 3 at a time

    // getImage :: String -> Promise
    let getImage = url=> makeRequest(url).then(data => data.base64, reqErrorHandler)
    
    // actions :: [(* -> Promise)]
    let actions = [
      ()=> getImage('one.jpg'),
      ()=> getImage('two.jpg'),
      ()=> getImage('three.jpg'),
      ()=> getImage('four.jpg'),
      ()=> getImage('five.jpg')
    ]
    
    // throttle the actions then do something...
    throttlep (3) (actions) .then(results => {
      // results are guaranteed to be ordered the same as the input array
      console.log(results)
      // [, , , , ]
    })
    

提交回复
热议问题