ES6 Promise replacement of async.eachLimit / async.mapLimit

前端 未结 4 957
南方客
南方客 2020-12-03 22:54

In async, if I need to apply a asynchronousfunction to 1000 items, I can do that with:

async.mapLimit(items, 10, (item, callback) => {
    foo(item, callb         


        
相关标签:
4条回答
  • 2020-12-03 23:05

    Using Array.prototype.splice

    while (funcs.length) {
      await Promise.all( funcs.splice(0, 100).map(f => f()) )
    }
    
    0 讨论(0)
  • 2020-12-03 23:07

    There's nothing built in, but you can of course group them yourself into promise chains, and use a Promise.all on the resulting array of chains:

    const items = /* ...1000 items... */;
    const concurrencyLimit = 10;
    const promise = Promise.all(items.reduce((promises, item, index) => {
        // What chain do we add it to?
        const chainNum = index % concurrencyLimit;
        let chain = promises[chainNum];
        if (!chain) {
            // New chain
            chain = promises[chainNum] = Promise.resolve();
        }
        // Add it
        promises[chainNum] = chain.then(_ => foo(item));
        return promises;
    }, []));
    

    Here's an example, showing how many concurrent promises there are any given time (and also showing when each "chain" is complete, and only doing 200 instead of 1,000):

    const items = buildItems();
    const concurrencyLimit = 10;
    const promise = Promise.all(items.reduce((promises, item, index) => {
        const chainNum = index % concurrencyLimit;
        let chain = promises[chainNum];
        if (!chain) {
            chain = promises[chainNum] = Promise.resolve();
        }
        promises[chainNum] = chain.then(_ => foo(item));
        return promises;
    }, []).map(chain => chain.then(_ => console.log("Chain done"))));
    promise.then(_ => console.log("All done"));
    
    function buildItems() {
      const items = [];
      for (let n = 0; n < 200; ++n) {
        items[n] = n;
      }
      return items;
    }
    
    var outstanding = 0;
    function foo(item) {
      ++outstanding;
      console.log("Starting " + item + " (" + outstanding + ")");
      return new Promise(resolve => {
        setTimeout(_ => {
          --outstanding;
          console.log("Resolving " + item + " (" + outstanding + ")");
          resolve(item);
        }, Math.random() * 500);
      });
    }
    .as-console-wrapper {
      max-height: 100% !important;
    }

    I should note that if you want to track the result of each of those, you'd have to modify the above; it doesn't try to track the results (!). :-)

    0 讨论(0)
  • 2020-12-03 23:19

    This is the closest one to async.eachLimit

    Promise.eachLimit = async (coll, limit, asyncFunc) => {
    let ret = [];
        const splitArr = coll.reduce((acc,item,i)=> (i%limit) ? acc :[...acc,coll.slice(i,i+limit)],[])
        for(let i =0; i< splitArr.length;i++){
            ret[i]=await Promise.all(splitArr[i].map(ele=>asyncFunc(ele)));
        }
        return ret;
    }
    
    const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
    
    async function foo(s) {
      await wait(Math.random() * 2000);
      console.log(s);
      return s.toLowerCase();
    }
    
    (async () => {
      let arr = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
      console.log((await Promise.eachLimit(arr, 5, foo)));
    })();

    0 讨论(0)
  • 2020-12-03 23:22

    If you don't care about the results, then it's quick to whip one up:

    Promise.eachLimit = async (funcs, limit) => {
      let rest = funcs.slice(limit);
      await Promise.all(funcs.slice(0, limit).map(async func => {
        await func();
        while (rest.length) {
          await rest.shift()();
        }
      }));
    };
    
    // Demo:
    
    var wait = ms => new Promise(resolve => setTimeout(resolve, ms));
    
    async function foo(s) {
      await wait(Math.random() * 2000);
      console.log(s);
    }
    
    (async () => {
      let funcs = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("").map(s => () => foo(s));
      await Promise.eachLimit(funcs, 5);
    })();

    A key performance property is running the next available function as soon as any function finishes.

    Preserving results

    Preserving the results in order makes it a little less elegant perhaps, but not too bad:

    Promise.mapLimit = async (funcs, limit) => {
      let results = [];
      await Promise.all(funcs.slice(0, limit).map(async (func, i) => {
        results[i] = await func();
        while ((i = limit++) < funcs.length) {
          results[i] = await funcs[i]();
        }
      }));
      return results;
    };
    
    // Demo:
    
    var wait = ms => new Promise(resolve => setTimeout(resolve, ms));
    
    async function foo(s) {
      await wait(Math.random() * 2000);
      console.log(s);
      return s.toLowerCase();
    }
    
    (async () => {
      let funcs = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("").map(s => () => foo(s));
      console.log((await Promise.mapLimit(funcs, 5)).join(""));
    })();

    0 讨论(0)
提交回复
热议问题