此脚本为我做在线瓦片地图中的部分代码,新的程序又需要同样的功能,整理下,贴出来。
1)基本介绍
功能:
主要功能是进行一次查询指令,这个查询指令包含多条子ajax的异步查询(项目中常见的场景是:需要多次查询,查询的结果进行汇总,然后再进行对数据的处理);
子查询中能够设定查询重实次数、超时时间等;
一次查询指令如果是一个整体事务,例如有一个子查询出错则,则可以终止整个查询指令;
一次查询指令具有独占性,即只能同时进行一次查询指令;
子查询结果状态检测;
查询和结果的配对:由于子查询是异步发送的,也就是返回结果顺序位置未知,因此进行了重新对应,即第一个QueryIetm对应的结果位第一条,以此类推。
补充:
关于查询的标准在很多较为成熟的语言中有很多实现参考,也已经成为标准,此处主要针对业务需要进行js封装;
代码中有很多啰嗦地方,暂时没有去优化,如:代码中getseter等等,当时主要为了js和as基本一套代码,所以罗嗦了;
为了保证能够运行,很多代码是从整个项目中的具体模块中移植过来,以后代码重构可使用seajs、requirejs等模块加载器。
2)几个文中的名词:
查询事务:即据有事务性质的查询,如果中断,则视为失败。
子查询:即为一条通常的ajax请求,此示例中均采用异步。对应的对象:es.QueryItem
查询命令:即一条查询指令,此指令包含多条子查询。对应的对象:es.QueryOrder
3)文件中类结构:
es.State:状态标识类
es.RequestMap:查询请求的Map实现类
es.RequestMapItem: Map中的Item
es.es.RequestMapItemBinding:MapItem所包装的数据类型
es.QueryOrder:查询命令类
es.QueryItem:子查询类
4)运行方式:
将此脚本引入你的页面中即可,由于项目中查询的发送返回结果是xml格式,因此此当前脚本只针对xml,其他类型在ajax请求和返回格式处稍做修改即可以。
举例:
var uQueryOrder = new es.QueryOrder();
var uQueryItem1 = new es.QueryItem("http://g1.ykimg.com/crossdomain.xml", "<a></a>");
uQueryOrder.addQueryItem(uQueryItem1);
var uQueryItem2 = new es.QueryItem("http://g1.ykimg.com/crossdomain.xml", "<b></b>");
uQueryOrder.addQueryItem(uQueryItem2);
var uQueryItem3 = new es.QueryItem("http://g1.ykimg.com/.xml", "<b></b>");
uQueryOrder.addQueryItem(uQueryItem3);
uQueryOrder.setTryTime(3);//如果查询失败,设置所有的子查询的重试次数为三次(也可以次查询进行单独设置,如果子查询也设置了重试次数则使用子查询的)
uQueryOrder.setFailureOnError(false);//设置查询命令是否为一个事务,即每个子查询均成功了,才认为是查询成功。
uQueryOrder.doQuery(); //进行查询
uQueryOrder.onFinished = function(){
for (var i=0; i<uQueryOrder.getQueryItemCount(); i++)
{
//如果子查询结果状态为SUCCESS时
if(uQueryOrder.getQueryItem(i).getResultState()){
var uRespXml=uQueryOrder.getQueryItem(i).getResponseXml();
alert(i+":" + uRespXml);
}
}
}
以下为代码:
/**
* @fileOverview es.queryorder.js
* @author withasi@163.com
* @version 1.0.1
*
*/
/** *********************以下代码为辅助性的,主要定义类创建,start********************** */
/**
* 类创建方法:
* Class1 = es.Class({
* initialize : function(p1){
* this._p1 = p1 || 2;
* },
* _p1:null,
* showP1 : function(){
* alert(this._p1);
* },
* CLASS_NAME : "Class1"
* });
*
* Class2 继承Class1
* Class2 = es.Class(Class1, {
* initialize : function(p21,p22){
* this._p21 = p21 || 22;
* this._p22 = p22 || 23;
* Class1.prototype.initialize.apply(this, [this._p21]);
* },
* _p21:null,
* _p22:null,
* CLASS_NAME : "Class2"
* });
*/
// 判定es命名空间是否被抢占,如果没有被抢占,则定义,否则则强制抢占(base脚本中有noConflict解决方法)
if (!es || Object.prototype.toString.call(es) !== "[object Object]")
var es = {};
es.Util = {};
es.Util.extend = function(destination, source) {
destination = destination || {};
if (source) {
for ( var property in source) {
var value = source[property];
if (value !== undefined) {
destination[property] = value;
}
}
/**
* IE doesn't include the toString property when iterating over an
* object's properties with the for(property in object) syntax.
* Explicitly check if the source has its own toString property.
*/
/*
* FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative
* prototype object" when calling hawOwnProperty if the source object is
* an instance of window.Event.
*/
var sourceIsEvt = typeof window.Event == "function"
&& source instanceof window.Event;
if (!sourceIsEvt && source.hasOwnProperty
&& source.hasOwnProperty('toString')) {
destination.toString = source.toString;
}
}
return destination;
};
es.Class = function() {
var Class = function() {
if (arguments && arguments[0] != es.Class.isPrototype) {
this.initialize.apply(this, arguments);
}
};
var extended = {};
var parent, initialize;
for ( var i = 0, len = arguments.length; i < len; ++i) {
if (typeof arguments[i] == "function") {
if (i == 0 && len > 1) {
initialize = arguments[i].prototype.initialize;
arguments[i].prototype.initialize = function() {
};
extended = new arguments[i];
if (initialize === undefined) {
delete arguments[i].prototype.initialize;
} else {
arguments[i].prototype.initialize = initialize;
}
}
parent = arguments[i].prototype;
} else {
parent = arguments[i];
}
es.Util.extend(extended, parent);
}
Class.prototype = extended;
return Class;
};
es.Class.isPrototype = function() {
};
es.Class.create = function() {
return function() {
if (arguments && arguments[0] != es.Class.isPrototype) {
this.initialize.apply(this, arguments);
}
};
};
es.Class.inherit = function() {
var superClass = arguments[0];
var proto = new superClass(es.Class.isPrototype);
for ( var i = 1, len = arguments.length; i < len; i++) {
if (typeof arguments[i] == "function") {
var mixin = arguments[i];
arguments[i] = new mixin(es.Class.isPrototype);
}
es.Util.extend(proto, arguments[i]);
}
return proto;
};
/**
* @description 类定义:设置状态
* @class 状态类
* @param {Boolean}
*
* @private
* @return {void}
*
*/
es.State = function(state) {
this.state = state;
};
/**
* @private
*/
es.State.ERROR = false;
/**
* @private
*
*/
es.State.SUCCESS = true;
/**
* @description: 请求的Map辅助类
* @private
*/
es.RequestMap = es.Class({
initialize: function(){},
map : [],
/**
* @private
*/
getValue : function(vRequest) {
for ( var i = 0; i < this.map.length; i++) {
if (this.map[i].request == vRequest) {
return this.map[i].binding;
}
}
return null;
},
/**
* @private
*/
add : function(vRequest, vBinding) {
this.map[this.map.length] = new es.RequestMapItem(vRequest, vBinding);
},
/**
* @private
*/
remove : function(vRequest) {
for ( var i = 0; i < this.map.length; i++) {
if (this.map[i].request == vRequest) {
this.map[i] == null;
return;
}
}
}
});
/**
* @description: 请求的RequestMapItem辅助类,你RequestMap中的一条item
* @private
*/
es.RequestMapItem = es.Class({
initialize: function(vRequest, vBinding){
this.request = vRequest;
this.binding = vBinding;
},
request:null,
binding:null
});
/**
* @description: 请求的RequestMapItem类包装的条目内容,相当于一个结构体,一个数据类型
* @private
*/
es.RequestMapItemBinding = es.Class({
initialize: function(num, text){
this.num = num;
this.text = text;
},
num:null,
text:null,
/**
* @private
*/
toString:function(){
return "[" + this.num + "] == [" + this.text + "]";
}
});
/** *********************end********************** */
/**
* @description 定义一个查询命令类
* @class 查询命令类,即次查询可包含多条子查询
* @param {Function} finishedCallback 查询命令回调函数<br/>
* 参数省略:可以省略
*
* @example new es.QueryOrder(function(e){});
*
*/
es.QueryOrder = es
.Class({
initialize : function(finishedCallback) {
if(Object.prototype.toString.call(finishedCallback) === "[object Function]")this.onFinished = finishedCallback;
},
/** ************以下是属性****************** */
/**
* @description 属性含义:查询子对象数组 初始默认值:无
* @return {Array}
* @field
*/
requestMapObject : new es.RequestMap(),
/**
* @description 属性含义:查询子对象数组 初始默认值:无
* @return {Array}
* @field
*/
queryItem : [],
/**
* @description 属性含义:设置的子查询超时时间(针对子查询QueryItem的,每个子查询也可以设置此属性,此处主要是为了方便,相当于一个批属性)
* 初始默认值:2000毫秒
* @return {Int}
* @field
*/
timeOut : 2000,
/**
* @description 属性含义:设置子查询重试次数(针对子查询QueryItem的,每个子查询也可以设置此属性,此处主要是为了方便,相当于一个批属性)
* 初始默认值:2
* @return {Int}
* @field
*/
tryTime : 2,
/**
* @description 属性含义:查询命令是不是事务
* 初始默认值:false,即不是查询事务,例如一条查询命令包含10个子查询,如果其中两条出错或者超时,其余8条子查询照样执行,但是这条查询命令的查询成功状态为false
* @return {Boolean}
* @field
*/
failureOnError : false,
/**
* @description 属性含义:记录子查询的个数
* @初始默认值:无
* @return {Int}
* @field
*/
itemCount : null,
/**
* @description 属性含义:当前当前已经未成功查询的子查询对象个数 初始默认值:无
* @return {Int}
* @field
*/
itemCurrentCount : null,
/**
* @description 属性含义:设置的一个查询对象的出错标记
* @初始默认值:0
* @return {Int}
* @field
*/
queryFlag : 0,
/**
* @description 函数描述:添加子查询对象
* @param {Object}
* queryItem 参数描述:子查询 参数省略:不可省略
* @return {void}
*/
addQueryItem : function(queryItem) {
/* 定义这个实例变量的目的是在子查询中能够拿到查询命令对象 */
queryItem.queryOrderPeer = this;
this.queryItem.push(queryItem);
this.itemCount++;
},
/**
* @description 函数描述: 获取查询超时时间,返回超时时间
* @return {Int}
*/
getTimeout : function() {
return this.timeOut;
},
/**
* @description 函数描述:设置查询超时时间
* @param {String}
* timeOut 参数含义:超时时间
* @return {void}
*/
setTimeout : function(timeOut) {
if(timeOut>0){
this.timeOut = timeOut;
}
},
/**
* @description 函数描述:获取出错查询次数,返回重试的次数
* @return {Int}
*/
getTryTime : function() {
return this.tryTime;
},
/**
* @description 函数描述:设置查询出错允许次数
* @param {Int}
* tryTime 参数含义:重试次数
* @return {void}
*/
setTryTime : function(tryTime) {
if(tryTime>=0){
this.tryTime = tryTime;
}
},
/**
* @description 函数描述:判断一个查询对象是不是一个事务,如果是一个事务,则有一个子查询
* 出错次数超过设置的出错超时次数,则设置停止此查询命令,返回查询的是否为事务
* @return {Boolean}
*/
getFailureOnError : function() {
return this.failureOnError;
},
/**
* @description 函数描述:设置一个查询对象是是不是一个事物,默认设置为False
* @param {Boolean}
* failureOnError 参数含义:查询对象是否是事务
* @return {void}
*/
setFailureOnError : function(failureOnError) {
this.failureOnError = failureOnError;
},
/**
* @description 函数含义:定义一个虚类,用户实现查询覆盖,一个查询回调函数
* @return{void}
*/
onFinished : function() {
},
/**
* @description 函数描述: 执行查询过程
* @return {void}
*/
doQuery : function() {
/* 如果当前的查询状态为false,则设置为true,表示正在进行查询 */
if (es.QueryOrder.queryState == false)
es.QueryOrder.QueryState = true;
else {
throw new Error(
"es.xmlparse.EzMapserviceQuery::doQuery查询还没有查询结束,不能进行下次查询");
}
/* 设置的查询未成功的个数 */
this.itemCurrentCount = this.itemCount;
/* 查询每一个子查询 */
for ( var i = 0; i < this.queryItem.length; i++) {
var item = this.queryItem[i];
/*如果子查询没有设置超时时间和重试次数,则用查询命令的中的值赋予每一个查询子对象中去 */
if(item.getTimeOut()>0){
item.setTimeOut(this.timeOut);
}
if(item.getTryTime()>=0){
item.setTryTime(this.tryTime);
}
/* 执行子查询过程 */
item.doQuery();
}
},
/**
* @description 函数描述:获取添加到查询对象中的子查询过的个数,返回子查询个数
* @return {Int}
*/
getQueryItemCount : function() {
return this.itemCount;
},
/**
* @description 函数描述:获取第i个子查询,返回子查询对象
* @param {Int}
* i 参数含义:第i个子查询
* @return {Object}
*/
getQueryItem : function(i) {
return this.queryItem[i];
},
CLASS_NAME : "es.QueryOrder"
});
/**
* @description 属性含义:这是一个静态的属性,查询对象的状态:默认是false,不在查询过程中
* @初始默认值:false
* @return {Boolean}
* @field
* @static
*/
es.QueryOrder.queryState = false;
/**
* @description 属性描述:设置的查询的全局变量,记录已经发送的查询的子对象 默认初始值:0
* @return {Int}
* @field
* @static
*/
es.QueryOrder.NextOrder = 0;
/**
* @description 定义一个查询类
* @class 子查询类
*
*/
es.QueryItem = es
.Class({
initialize : function(url, xmlDoc) {
this.url = url;
this.xmldoc = xmlDoc;
},
/**
* @description 属性描述:查询超时时间 初始默认值:无
* @return {Int}
* @field
*/
timeOut : null,
/**
* @description 属性描述:设置的出错一共能查询的次数 初始默认值:无
* @return {Int}
* @field
*/
tryTime : null,
/**
* @description 属性描述:设置的默认的子查询状态 初始默认值:true
* @return {Boolean}
* @field
*/
state : es.State.SUCCESS,
/**
* @description 属性描述:传入的url 初始默认值:无
* @return {String}
* @field
*/
url : null,
/**
* @description 属性描述:传入的XMl 初始默认值:无 return{String}
* @field
*/
xmlDoc : null,
/**
* @description 属性描述:返回的Xml 初始默认值:无
* @return {String}
* @field
*/
responseXml : null,
/**
* @description 属性含义:当前查询次数变量 初始默认值:0
* @return {Int}
* @field
*/
currentTryTime : 0,
/**
* @description 属性含义:重试ID 初始默认值:0
* @return {Int}
* @field
*/
reSendId : 0,
/**
* 函数功能:获取超时时间,返回超时时间
*
* @return{Int}
*/
getTimeOut : function() {
return this.timeOut;
},
/**
* 函数功能:设置超时时间
*
* @param {Int}
* timeOut 参数含义:超时时间
* @return {void}
*/
setTimeOut : function(timeOut) {
if(timeOut>0){
this.timeOut = timeOut;
}
},
/**
* 函数功能:获取重试的次数,返回重试次数
*
* @return {Int}
*/
getTryTime : function() {
return this.tryTime;
},
/**
* 函数功能:设置重试的次数
*
* @param {Int}
* tryTime 参数含义:重试次数
* @return {void}
*/
setTryTime : function(tryTime) {
if(tryTime>=0){
this.tryTime = tryTime;
}
},
/**
* 函数功能:获取请求的Xml,返回请求的Xml
*
* @return {String}
*/
getQueryXml : function() {
return this.xmlDoc;
},
/**
* 函数功能:设置请求的xml
*
* @param {String}
* xmlDoc 参数含义:请求的Xml
* @return {void}
*/
setQueryXml : function(xmlDoc) {
this.xmlDoc = xmlDoc;
},
/**
* 函数功能:获取返回的xml,返回从服务器接收的Xml
*
* @return {String}
*/
getResponseXml : function() {
return this.responseXml;
},
/**
* 函数功能:设置返回的Xml
*
* @param {String}
* responseXml 参数含义:从服务器接收的Xml
* @return {void}
*/
setResponseXml : function(responseXml) {
this.responseXml = responseXml;
},
/**
* 函数功能:返回结果状态 ,返回子查询的状态
*
* @return {Boolean}
*/
getResultState : function() {
return this.state;
},
/**
* 函数功能:设置返回结果状态
*
* @param {Boolean}
* state 参数描述:子查询状态
* @return {void}
*/
setResultState : function(state) {
this.state = state;
},
/**
* 函数功能:执行子查询
*
* @return {void}
*/
doQuery : function() {
var peer = this;
var text2Send = this.xmlDoc;
this.itemId = null;
var xmlhttp = window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();//此处需要在IE浏览器各个版本需要进一步细分。
xmlhttp.open("POST", this.url, true);
xmlhttp.setRequestHeader('Content-Type', 'text/xml');
xmlhttp.onreadystatechange = HandleStateChange;
/* 如果子查询没有重试,则将序号为NextOrder的子查询加入RequestMapObject中保存 */
if (peer.currentTryTime == 0)
peer.queryOrderPeer.requestMapObject.add(xmlhttp, new es.RequestMapItemBinding(
es.QueryOrder.NextOrder, text2Send));
/* 如果子查询重试,则将重试的ID号peer.reSendId的子查询覆盖原有的RequestMapObject中元素 */
else
peer.queryOrderPeer.requestMapObject.add(xmlhttp, new es.RequestMapItemBinding(
peer.reSendId, text2Send));
xmlhttp.send(text2Send);
/* 只有没有重试,es.QueryOrder.NextOrder++才增加 */
if (peer.currentTryTime == 0)
es.QueryOrder.NextOrder++;
/* 回调函数 */
function HandleStateChange() {
if (xmlhttp.readyState == 4) {
// 返回状态为200的情况
if (xmlhttp.status == 200) {
var doc1 = null;
try {
doc1 = new ActiveXObject("Microsoft.XMLDOM");
doc1.async = false;
if (xmlhttp.responseText != null) {
doc1.loadXML(xmlhttp.responseText);
} else
return;
} catch (e) {
try // Firefox, Mozilla, Opera, etc.
{
parser = new DOMParser();
if (xmlhttp.responseText != null) {
doc1 = parser.parseFromString(xmlhttp.responseText,
"text/xml");
} else
return;
} catch (e) {
alert(e.message);
}
}
if ((doc1.getElementsByTagName("ERROR").length == 0)) { // 返回的xml没有错误信息
peer.queryOrderPeer.requestMapObject.remove(xmlhttp);
peer.setResponseXml(xmlhttp.responseText); // 设置返回信息
peer.setQueryXml(peer.queryOrderPeer.requestMapObject
.getValue(xmlhttp).text); // 获取发送的xml的内容
peer.itemId = peer.queryOrderPeer.requestMapObject.getValue(xmlhttp).num; // 获取发送的xml的序号
if (--peer.queryOrderPeer.itemCurrentCount == 0) { // 所有子查询都完成的情况
es.QueryOrder.QueryState = false; // 表明此时这个查询对象已经结束
peer.queryOrderPeer.onFinished();
}
} else { // 返回的xml包含错误信息
if (++peer.currentTryTime > peer
.getTryTime()) { // 判断此时当前重查次数是否超过设定的重查次数
peer.setResultState(es.State.ERROR); // 设置子查询出错状态
peer.queryOrderPeer.queryFlag++;
--peer.queryOrderPeer.itemCurrentCount;
if (peer.queryOrderPeer.failureOnError // 该查询对象是一个事务,且子查询中出错,直接抛错
&& peer.queryOrderPeer.queryFlag == 1) {
es.QueryOrder.QueryState = false;
throw new Error("由于第"
+ peer.queryOrderPeer.requestMapObject.getValue(xmlhttp).num
+ "个子查询出错,该查询事务终止");
}
peer.setResponseXml(xmlhttp.responseText); // 设置返回信息
if (peer.queryOrderPeer.itemCurrentCount == 0) { // 子查询已经查完了,现在结束查询过程,返回onFinished()过程,交给用户获取所需要的信息
es.QueryOrder.QueryState = false;
peer.queryOrderPeer.onFinished();
}
return;
}
peer.doQuery(); // 重查
}
} else { // 返回信息不实200的其他状态,得不到正确的xml信息,设置子查询error,将不会返回给用户信息(null)
if (++peer.currentTryTime > peer
.getTryTime()) {
peer.setResultState(es.State.ERROR);
peer.queryOrderPeer.queryFlag++;
--peer.queryOrderPeer.itemCurrentCount;
if (peer.queryOrderPeer.failureOnError
&& peer.queryOrderPeer.queryFlag == 1) {
es.QueryOrder.QueryState = false;
throw new Error("由于第"
+ peer.queryOrderPeer.requestMapObject.getValue(xmlhttp).num
+ "个子查询出错,该查询事务终止");
}
if (peer.queryOrderPeer.itemCurrentCount == 0) {
es.QueryOrder.QueryState = false;
peer.queryOrderPeer.onFinished();
}
return; //
}
peer.doQuery();
}
}
}
},
CLASS_NAME : "es.QueryItem"
});