【问题标题】:Performance considerations with `Promise.all` and a large amount of asynchronous operations`Promise.all` 和大量异步操作的性能考虑
【发布时间】:2015-06-25 15:03:39
【问题描述】:

Promise.all 与异步代码一起使用时(在同步代码的情况下,无需担心),当您想要发送一大堆时,您可能会遇到严重的性能(如果不是其他类型)问题(可能是数十、数百、数千甚至数百万)请求,因为您的异步操作(例如本地文件系统、HTTP 服务器、数据库等)的接收端无法正常处理那么多并行请求。

对于这种情况,如果我们能告诉Promise.all 我们希望同时进行多少个承诺,那将是完美的。但是,既然 A+ 应该是精简的,那么添加这些花哨的功能肯定没有意义。

那么,实现这一目标的更好方法是什么?

【问题讨论】:

    标签: javascript performance promise bluebird


    【解决方案1】:

    嗯,首先 - 不可能给 Promise.all 一个并发参数,因为 Promise 代表 已经开始 操作,所以你不能将它们排队或在执行前等待。

    您希望在有限并发下执行的是承诺返回函数。幸运的是 - bluebird 使用 Promise.map 提供了这个功能(从 2.x 版开始):

     Promise.map(largeArray, promiseReturningFunction, {concurrency: 16});
    

    并发参数决定一次可以发生多少个操作——注意这不是一个全局值——而只针对这个链。例如:

    Promise.map([1,2,3,4,5,6,7,8,9,10], function(i){
        console.log("Shooting operation", i);
        return Promise.delay(1000);
    }, {concurrency: 2});
    

    Fiddle

    请注意,不保证执行顺序。

    【讨论】:

    • 太棒了!感谢捆绑!不太好的是我在自己的事情上浪费了时间......但你生活和学习!
    • @Domi 当 bluebird 3.0 到来时(很快!),文档在专门的文档网站上会变得更好。抱歉,这并不明显 - 随时编辑文档并提出拉取请求,这些都是受欢迎的。
    • 为什么Promise.map不能保证执行顺序?
    • Promises 已经开始操作了,我不明白你的问题。
    • @Domi 我明白了,bluebird 没有这种功能 - 但您始终可以将 .map 与虚拟值一起使用或滚动您自己的值。
    【解决方案2】:

    由于我无法找到一个预先存在的库来处理 Promise 批处理,我自己编写了一个简单的原语。它是一个封装了要执行的functions 数组的类,并将其划分为给定大小的批次。它将等待每个批次完成,然后再运行下一个批次。这是一个相当幼稚的实现。在某些网络场景中可能需要一个成熟的限制机制。

    Fiddle.

    代码:

    /**
     * Executes any number of functions, one batch at a time.
     *
     * @see http://jsfiddle.net/93z8L6sw/2
     */
    var Promise_allBatched = (function() {
        var Promise_allBatched = function(arr, batchSize) {
            if (arr.length == 0) return Promise.resolve([]);
            batchSize = batchSize || 10;
    
            var results = [];
            return _runBatch(arr, batchSize, results, 0)
            .return(results);        // return all results
        };
    
        function _runBatch(arr, batchSize, results, iFrom) {
            // run next batch
            var requests = [];
            var iTo = Math.min(arr.length, iFrom + batchSize);
    
            for (var i = iFrom; i < iTo; ++i) {
                var fn = arr[i];
                var request;
                if (fn instanceof Function) {
                    request = fn();
                }
                else {
                    request = fn;
                }
                requests.push(request);            // start promise
            }
    
            return Promise.all(requests)        // run batch
            .then(function(batchResults) {
                results.push.apply(results, batchResults);    // store all results in one array
    
                console.log('Finished batch: ' + results.length + '/' + arr.length);
            })
            .then(function() {
                if (iTo < arr.length) {
                    // keep recursing
                    return _runBatch(arr, batchSize, results, iTo);
                }
            });
        }
    
        return Promise_allBatched;
    })();
    

    【讨论】:

    • 这个实现实际上非常漂亮,尤其是您如何避免在整个过程中创建延迟。请注意,这是分批运行的,所以它会运行 N 个项目,并且在所有 N 个项目都完成之前不会启动另一个项目。
    • @BenjaminGruenbaum 这正是我想做的。带有concurreny 参数集的Promise.map 与此不同吗?
    • 是的,它会确保只有 N 项目同时运行,但它会在第一个项目完成后立即启动 N+1th 项目 - 在某些情况下会更快。
    【解决方案3】:
    let arry=[1,2,4,........etc];//large number of array
    
     while (arry.length) {
        //I splice the arry 0,10...so you can change the limit as your //wish(note your server should be handle your limit
      Promise.all(arry.splice(0, 10).map(async (eachArryElement) => {
                       let res = await yourAsyncMethod(eachArryElement);
        }))
               
    }
    
    function yourAsyncMethod(data){
     return new Promise((resolve, reject) => {
     //your logic
     resolve('your output')
       })
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多