簡體   English   中英

在 Node js 中限制 Q 承諾並發

[英]Limit Q promise concurrency in Node js

有沒有辦法限制節點js中一次執行的並發Q承諾的數量?

我正在構建一個網絡爬蟲,它必須請求和解析更多 3000 多個頁面,並且在沒有限制的情況下,我提出的一些請求沒有及時響應,因此連接停止,所需的響應(html 代碼)變得不可用。

為了解決這個問題,我發現限制請求數量我的問題消失了。


我嘗試了以下方法但無濟於事:

我需要請求一個 url 數組,一次只執行 1 個請求,當數組中的所有 url 都完成時,然后將結果返回到一個數組中。

function processWebsite() {
  //computed by this stage
  urls = [u1,u2,u3,u4,l5,u6,u7,u8,u9];

  var promises = throttle(urls,1,myfunction);

  // myfunction returns a Q promise and takes a considerable 
  // amount of time to resolve (approximately 2-5 minutes)
  
  Q.all(promises).then(function(results){
      //work with the results of the promises array
  });
}

我會這樣做,它將遍歷每個 URL,構建一個在前一個完成時運行的承諾鏈,並使用請求結果數組進行解析。

return urls.reduce(function(acc, url){
    return acc.then(function(results)
        return myfunction(url).then(function(requestResult){
             return results.concat(requestResult)
        });
    });
}, Q.resolve([]));

你也可以把它變成一個幫手:

var results = map(urls, myfunction);

function map(items, fn){
    return items.reduce(function(acc, item){
        return acc.then(function(results)
            return fn(item).then(function(result){
                 return results.concat(result)
            });
        });
    }, Q.resolve([])
}

請注意, bluebird promise 庫有一個幫手來簡化這種事情。

return Bluebird.map(urls, myfunction, {concurrency: 1});

這是我為 Q 制作節流map函數的嘗試。

function qMap(items, worker, concurrent) {
    var result = Q.defer();
    var work = [];
    var working = 0;
    var done = 0;

    concurrent = parseInt(concurrent, 10) || 1;

    function getNextIndex() {
        var i;
        for (i = 0; i < items.length; i++) {
            if (typeof work[i] === "undefined") return i;
        }
    }
    function doneWorking() {
        working--;
        done++;
        result.notify( +((100 * done / items.length).toFixed(1)) );
        if (!startWorking() && done === items.length) {
            result.resolve(work);
        }
    }
    function startWorking() {
        var index = getNextIndex();
        if (typeof index !== "undefined" && working < concurrent) {
            working++;
            work[index] = worker(items[index]).finally(doneWorking);
            return true;
        }
    }
    while (startWorking());
    return result.promise;
}

它接受

  • 要處理的items數組(在您的情況下為 URL),
  • 一個worker (它必須是一個接受項目並返回承諾的函數)
  • 以及在任何給定時間處理的concurrent項目的最大值。

它返回

  • 一個承諾和
  • 當所有工人都完成時,解析為一系列已解決的承諾。

它不會失敗,您必須檢查各個承諾以確定操作的整體狀態。

在您的情況下,您會像這樣使用它,例如有 15 個並發請求:

// myfunction returns a Q promise and takes a considerable 
// amount of time to resolve (approximately 2-5 minutes)

qMap(urls, myfunction, 15)
.progress(function (percentDone) {
    console.log("progress: " + percentDone);
})
.done(function (urlPromises) {
    console.log("all done: " + urlPromises);
});

你可以在then()塊中請求一個新的 url

myFunction(urls[0]).then(function(result) {
  myFunction(urls[1]).then(function(result) {
    myFunction(urls[2]).then(function(result) {
      ...
    });
  });
});

當然,這將是它的動態行為。 一旦承諾得到解決,我會維護一個隊列並讓一個 URL 出隊。 然后再提出一個請求。 並且可能有一個將 url 與結果相關聯的哈希對象。

第二個:

var urls = ...;
var limit = ...;
var dequeue = function() {
  return an array containing up to limit
};

var myFunction = function(dequeue) {
  var urls = dequeue();

  $q.all(process urls);
};

myFunction(dequeue).then(function(result) {
  myFunction(dequeue).then(function(result) {
    myFunction(dequeue).then(function(result) {
      ...
    });
  });
});

可以使用遞歸解決。

這個想法是,最初您發送最大允許數量的請求,並且這些請求中的每一個都應在完成時遞歸地繼續發送自身。

function processWebsite(urls, concurrentRequestsLimit) {
    return new Promise(resolve => {
        var pages = [];
        var index = 0;

        function recursiveFetch() {
            if (index === urls.length) {
                return;
            }
            fetch(urls[index++]).then(r => {
                pages.push(r.text());
                if (pages.length === urls.length) {
                    resolve(pages);
                } else {
                    recursiveFetch();
                }
            });
        }

        for (var i = 0; i < concurrentRequestsLimit; i++) {
            recursiveFetch();
        }
    });
}

var urls = [
    'http://www.example.com/page_1',
    'http://www.example.com/page_2',
    'http://www.example.com/page_3',
    ...
    'http://www.example.com/page_3000'
];
processWebsite(urls, 5).then(pages => {
   //process all 3000 pages here
});

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM