多ajax查询模拟一个整体的查询事务

此脚本为我做在线瓦片地图中的部分代码,新的程序又需要同样的功能,整理下,贴出来。

 

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"
});

 

posted @ 2011-11-11 18:43  @si  阅读(364)  评论(0编辑  收藏  举报