[英]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.