Node.js Promise.all 限制并发数量
為什么80%的碼農(nóng)都做不了架構(gòu)師?>>> ??
Promise.all 本身不負責執(zhí)行,執(zhí)行過程在傳遞給Promise.all之前已經(jīng)開始,Promise.all只等待全部執(zhí)行完成,執(zhí)行resolve,或碰到有執(zhí)行失敗,立即執(zhí)行reject部分。Promise.all非常好用,唯一的問題是,不能限制并發(fā)數(shù)量,所有任務同時開始執(zhí)行,因為Promise.all本身不負責執(zhí)行具體任務,所以也無法實現(xiàn)并發(fā)控制。
實現(xiàn)一個簡單的可以控制并發(fā)數(shù)量的Promise.allLimit函數(shù),可以通過參數(shù)來控制并發(fā)數(shù)量。代碼:
/* promise-limit.js */ /* jshint esversion: 6 */ /*jslint node: true */ Promise.allLimit = function(arr, wrap, limit, callback) {return new Promise((resolve, reject) => {var total = arr.length;var result = new Array(total);var rejected = false;var dones = 0;function run(n) {setTimeout(() => {wrap(n, arr.shift()).then(res => {return typeof callback === 'function' ? callback(n, res) : Promise.resolve(res);}).then(res => {dones++;result[n] = res;if (!rejected) {if (arr.length) {run(total - arr.length);} else if (dones === total) {resolve(result);}}}).catch(err => {rejected = true;reject(err);});}, 0);}arr.slice(0, limit).forEach((v, n) => {run(n);});}); };同樣返回一個Promise對象,可以直接替換Promise.all,不同的是,需要傳遞一個函數(shù)(wrap參數(shù)),用來包裹生成每一個具體執(zhí)行的Promise對象,limit用來限定并發(fā)數(shù)量,在指定并發(fā)任務內(nèi),一個任務完成后,再吸入一個新任務繼續(xù)執(zhí)行。
callback用來解析每一次任務完成后所需要的后續(xù)動作,比如存儲下載的內(nèi)容、或?qū)?shù)做變換,必須也返回一個Promise對象。
測試代碼:
Promise.allLimit([2000, 1500, 2500, 3000, 1500], function(n, time) {return new Promise((resolve, reject) => {console.log("Start Job: ", n, time);// setTimeout(2500 === time ? reject : resolve, time, "Time: " + time); // 測試rejectsetTimeout(resolve, time, "Time: " + time);}); }, 2, (n, res) => {// log Job n doneconsole.log("Done Job: ", res);return Promise.resolve(n); // log Job n done }).then(result => {console.log("All Done: ", result); }).catch(err => {console.log("Error: ", err); });最后貢獻一個下載妹子圖的簡單代碼,默認控制并發(fā)數(shù)量10個。
#!/usr/bin/env node/* jshint esversion: 6 */ /*jslint node: true */ require('./promise-limit.js');const FS = require('fs'); const PATH = require('path'); const UTIL = require('util'); const ARGV = require('yargs').argv; const REQUEST = require('request'); const CHEERIO = require('cheerio');if (!ARGV.url || !ARGV.dir || !/\/$/.test(ARGV.dir) || !ARGV.img || !ARGV.total || !ARGV.from || !ARGV.to) {console.log("usage: --url http://example.com --dir ./imgs/ --img '.main-image img' --total '共(\d+)頁' --from '.php' --to '_%d.php' ");process.exit(); }const parallel = ARGV.parallel || 10; //并發(fā)數(shù)量const headers = {'Referer': ARGV.url,'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8','Accept-Encoding': 'gzip, deflate, sdch','Accept-Language': 'en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4','User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.104 Safari/537.36' };function fetch(url, encoding = 'utf8') {return new Promise((resolve, reject) => {REQUEST({url: url,headers: headers,gzip: true,encoding: encoding}, (error, response, body) => {if (error) {reject(error);} else {resolve(body);}});}); }function write(file, content) {return new Promise(function(resolve, reject) {FS.writeFile(file, content, function(err) {if (err) {reject(err);} else {resolve();}});}); }fetch(ARGV.url).then(rsp => {const total = parseInt((rsp.match(new RegExp(ARGV.total)) || [0, 0])[1]);if (!total) {throw new Error('Match total error');}console.log("Total: %d, Parallel: %d", total, parallel);return Promise.allLimit(Array.from({length: total}, (v, k) => k + 1), (k, v) => {return fetch(v === 1 ? ARGV.url : ARGV.url.replace(ARGV.from, UTIL.format(ARGV.to, v)));}, parallel, (k, res) => {let src = CHEERIO.load(res)(ARGV.img).attr('src');let file = (k + 1) + PATH.extname(src);return fetch(src, null).then(img => {return write(ARGV.dir + file, img);}).then(() => {console.log("OK: [" + file + "]\t" + src);return file;}).catch(err => {console.log("ER: [" + file + "]\t" + src + " : " + err.toString());});});}).then(rsp => {console.log("All Jobs Done: ");console.log(rsp);}).catch(err => {console.log("Fetch failed: %s", err.toString());});執(zhí)行:
./request.js --total '(\d+)</span></a><a[^<>]+><span>下一頁' --from '/71636' --to '/71636/%d' --img '.main-image img' --dir ./71636/ --url 'http://www.mzitu.com/71636' --parallel 10轉(zhuǎn)載于:https://my.oschina.net/jsk/blog/789877
總結(jié)
以上是生活随笔為你收集整理的Node.js Promise.all 限制并发数量的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 复杂多边形光栅化算法
- 下一篇: 利用奇异值分解(SVD)简化数据