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

  • 相关阅读:
    Android Listview 隐藏滚动条
    打开Activity时,不自动显示(弹出)虚拟键盘
    Spring Boot web API接口设计之token、timestamp、sign
    WPF ListView点击删除某一行并获取绑定数据
    WPF中控件的显示与隐藏
    WPF 格式化输出- IValueConverter接口的使用 datagrid列中的值转换显示
    WPF之DataGrid应用 翻页
    WPF中修改DataGrid单元格值并保存
    DataGrid获取单元格的值
    WPF DataGrid 列宽填充表格方法
  • 原文地址:https://www.cnblogs.com/withasi/p/2481039.html
Copyright © 2020-2023  润新知