在实际应用中,我们总会遇到以下类似的情况:
- 在构建爬虫时,需要对当前页面下的所有分页爬取结果页,并在结果页爬取相关数据.
- 在发送结果数据时,需要按一个固定的队列数量,分次发送数据.
- 在执行数据库操作时,需要固定数量插入.
这种分页型任务在很多应用中都需要自己实现.下面,本文将讨论具体的实现方法:
在之前的文章中,我们使用promise
的传递特性来实现中间件部分的功能.今天,我们将使用promise
的特性用一个简单的函数来实现上文需要的分页任务
方法.
实现思路
在promise
语法中,我们拥有Promise.all
方法,通过Promise.all,我们可以让一串任务(数组)同时执行,并在所有任务执行完成后返回任务顺序相同的结果数组,如下所示:
let promiseList=[new Promise(function(resolve,reject){
//do something
},new Promise()(function(resolve,reject){
//do something
})];
Promise.all(promiseList).then(function(result){
//Use result[0] & result[1] to get promise func result
}).catch(function(err){
//Get error info
})
通过使用Promise.all
,我们就可以使用如下方法来组合一个分页任务加以执行了.
函数方法
/**
* 请求下个Promise
* @param {Object} err 错误信息
* @param {Object} data 传递数据
* @return {Object} Promise对象
*/
function nextPromise(err,data){
return new Promise(function(resolve,reject){
if(err) reject(err);
else resolve(data);
});
}
/**
* 创建Promise分页执行列表
* @param {Array} promiseFuncList Promise函数列表
* @param {Number} pageSize 单页个数
* @return {Object} Promise对象
*/
exports.buildPromiseListByPage = function(promiseFuncList,pageSize){
if(promiseFuncList.length===0) return nextPromise();
let resultData=[];
let roundCount=0;
let totalRound=Math.ceil(promiseFuncList.length/pageSize);
let promiseFunc=nextPromise();
for(let i=0;i<totalRound;i++){
promiseFunc=promiseFunc.then(function(){
let promiseList=promiseFuncList.slice(i*pageSize,(i+1)*pageSize).map(function(curFunc){
return curFunc();
});
return Promise.all(promiseList);
});
}
return promiseFunc;
};
通过使用buildPromiseListByPage
方法,我们可以快速创建一个分页发送任务.
如果你需要获取结果数据的话,就需要改造一下
buildPromiseListByPage
函数.把结果最终传递出去.
/**
* 创建Promise分页执行列表
* @param {Array} promiseList Promise对象列表
* @param {Number} pageSize 单页个数
* @return {Object} Promise对象
*/
exports.buildPromiseListByPage = function(promiseFuncList,pageSize){
if(promiseFuncList.length===0) return nextPromise();
let resultData=[];
let roundCount=0;
let totalRound=Math.ceil(promiseFuncList.length/pageSize);
let promiseFunc=nextPromise();
for(let i=0;i<totalRound;i++){
promiseFunc=promiseFunc.then(function(){
let promiseList=promiseFuncList.slice(i*pageSize,(i+1)*pageSize).map(function(curFunc){
return curFunc();
});
return Promise.all(promiseList)
.then(function(result){
resultData=resultData.concat(result);
});
});
}
return promiseFunc;
};
测试用例
最后,我们写个测试用例来测试是否和我们预期的一致.
为了确认是否按我们预期的轮次执行,我们加入了一个roundCount变量来做循环次数统计
func.js
'use strict';
/**
* 请求下个Promise
* @param {Object} err 错误信息
* @param {Object} data 传递数据
* @return {Object} Promise对象
*/
function nextPromise(err,data){
return new Promise(function(resolve,reject){
if(err) reject(err);
else resolve(data);
});
}
exports.nextPromise=nextPromise;
/**
* 创建Promise分页执行列表
* @param {Array} promiseFuncList Promise函数列表
* @param {Number} pageSize 单页个数
* @return {Object} Promise对象
*/
exports.buildPromiseListByPage = function(promiseFuncList,pageSize){
if(promiseFuncList.length===0) return nextPromise();
let resultData=[];
let roundCount=0;
let totalRound=Math.ceil(promiseFuncList.length/pageSize);
let promiseFunc=nextPromise();
for(let i=0;i<totalRound;i++){
promiseFunc=promiseFunc.then(function(){
let promiseList=promiseFuncList.slice(i*pageSize,(i+1)*pageSize).map(function(curFunc){
return curFunc();
});
return Promise.all(promiseList)
.then(function(result){
roundCount++;
resultData=resultData.concat(result);
});
});
}
return promiseFunc.then(function(){
return nextPromise(null,[roundCount].concat(resultData));
});
};
test.js
下面使用mocha来写一个测试用例,文件放在./test/test.js
.
'use strict';
const func = require('../func.js');
describe('function', function(){
it('Call buildPromiseListByPage', function (done) {
let promiseList=[];
let testString='test string';
let testObject={'test':'test'};
let testArray=[1];
let testObjectArray=[testObject];
promiseList.push(function(){
return func.nextPromise(null,1);
});
promiseList.push(function(){
return func.nextPromise(null,testString);
});
promiseList.push(function(){
return func.nextPromise(null,testObject);
});
promiseList.push(function(){
return func.nextPromise(null,testArray);
});
promiseList.push(function(){
return func.nextPromise(null,testObjectArray);
});
func.buildPromiseListByPage(promiseList,3)
.then(function(results){
let validResult=results.every(function(result,index){
switch(index){
case 0:
return result===2;
case 1:
return result===1;
case 2:
return result===testString;
case 3:
return result===testObject;
case 4:
return result===testArray;
case 5:
return result===testObjectArray;
}
});
if(!validResult) throw new Error('不符合预期的测试结果');
done();
}).catch(function(err){
done(err);
})
});
it('Call buildPromiseListByPage with empty', function (done) {
let promiseList=[];
func.buildPromiseListByPage(promiseList,3)
.then(function(result){
if(result){
done(new Error('不符合预期的测试结果'));
}else{
done();
}
})
.catch(function(err){
done(err);
})
});
it('Call buildPromiseListByPage with error', function (done) {
let promiseList=[];
promiseList.push(function(){
return func.nextPromise(new Error('测试错误'));
});
func.buildPromiseListByPage(promiseList,3)
.then(function(){
done(new Error('不符合预期的测试结果'));
})
.catch(function(err){
if(err.message==='测试错误') done();
else done(err);
})
});
});