Open AlexZ33 opened 3 years ago
/** * 类名 : scripts/utils/tiny-ajax * 描述: 提供页面ajax请求和数据缓存 * 1、相同ajax发出请求时,拦截第二到N次的请求,待第一次请求回来后,分发数据至所有请求,并自动调用各自的回调 * 2、数据已经返回后,发出的请求会自动到缓存里获取,数据超时后,请求会发至服务器请求 * 创建人: * 创建日期: 2017/12/5 * * 修改历史 * 修改日期: <修改日期,> * 修改人: * 修改原因/修改内容: <修改原因描述> <问题单号: ${问题单号}> */ define('utils/tiny-ajax', ['utils/tiny-promise', 'libs/moment-2.6.0'], (Promise) => { const _cache = {}; const _cache1 = []; /* window._cache = _cache; window._cache1 = _cache1; */ /** * * 获取csrf token信息 * @returns {*} */ function get_srf_toke() { const $_csrf_token = $('#_csrf_token'); const obj = {}; if ($_csrf_token.length) { obj.name = $_csrf_token.attr('name'); obj.val = $_csrf_token.val(); return obj; } return null; } /** * 执行response返回的脚本 * @param responseText * @private */ const _401_redirect = function (responseText) { try { window.tinyWidget && tinyWidget.session && tinyWidget.session.show(); } catch (e) { console.log('返回信息异常'); } }; /** * ajax错误拦截设置 */ $(document).ajaxError((event, request, settings) => { if (request.status == 401) { _401_redirect(request.responseText); } }); /** * jquery ajax crsf 设置 */ (function () { const crsf = get_srf_toke(); if (crsf) { $.ajaxSetup({ beforeSend(xhr) { if (crsf.name) { xhr.setRequestHeader(crsf.name, crsf.val); } } }); } }()); const ajax = { _cacheId: 0, /** * 设置angualr $http请求的token * @returns {*} */ setCrsfToken() { const crsf = get_srf_toke(); if (crsf) { const anCrsf = { headers: {} }; anCrsf.headers[crsf.name] = crsf.val; return anCrsf; } return null; }, clearCache() { /* for(let step = 0; step < _cache1.length ;step++){ let item = _cache1[step]; if(item._realPromise) } */ for (const key in _cache) { delete _cache[key]; } }, /** * * 缓存数据,并定时清除,以待下次获取最新数据 * @param key * * @returns {*} * */ data(key) { if (!_.isUndefined(arguments[1])) { _cache[key] = arguments[1]; return; } return _cache[key]; }, /** * 通过传入angular的$http,再次对返回数据做缓存处理 * @param $http * @param type 请求类型 * @param successFlag 成功判断标识字段 * @param options * options.url 请求url * options.data 请求参数 * options.type 请求类型 * options.expire 缓存数据时间 * options.dataType "xml": 返回 XML 文档,可用 jQuery 处理。 "html": 返回纯文本 HTML 信息;包含的 script 标签会在插入 dom 时执行。 "script": 返回纯文本 JavaScript 代码。不会自动缓存结果。除非设置了 "cache" 参数。注意:在远程请求时(不在同一个域下),所有 POST 请求都将转为 GET 请求。(因为将使用 DOM 的 script标签来加载) "json": 返回 JSON 数据 。 "jsonp": JSONP 格式。使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数。 "text": 返回纯文本字符串 * options.cacheAfter 缓存请求后需要添加的参数 * @param successFlag 成功判断标识字段 * * @returns promise */ getHttpRequest(options, successFlag) { /** * @Author TangChengchuan * @Date 2020/3/2 * @Description 将模型请求去重提取到此部分进行添加 */ if (!options || !options.url) { return; } const { url } = options; const type = options.type || options.method || 'get'; const data = options.data || (type === 'get' ? '' : {}); const expire = options.expire || 24 * 60 * 1000; const dataType = options.dataType || 'json'; const cacheAfter = options.cacheAfter || {}; // 缓存请求后添加参数 let promise; const filter = data !== '' ? JSON.stringify(data) : ''; // 数据data缓存key值--(url+updateType+data),包含了请求条件 不包含模型 const urlId = `${url}_${cacheAfter.updateType}_${filter}`; // 组件promise缓存key值--(pluginId+url+updateType), 组件 + url + 模型类型 来组成id区分组件,并单独对比参数 const pluginId = `${cacheAfter.pluginid}_${url}_${cacheAfter.updateType}`; // 数据promise缓存key值--(url+updateType+data+'-promise'), 包含了请求条件 不包含模型 const urlIdPromise = `${urlId}-promise`; // 获取数据data缓存 const lastData = this.data(urlId); // 获取组件promise缓存 const fromPlugin = this.data(pluginId); // 获取数据promise缓存 let fromUrl = this.data(urlIdPromise); if (lastData) { // 当存在数据缓存时直接返回数据。将改写的promise返回 promise = Promise.createPromise(); requestAnimationFrame(() => { promise.execute(lastData, true); }); // 需要查找是否存在已知的请求,已经找到数据缓存且存在promise缓存说明filter不同 if (fromPlugin) { fromPlugin.abort(); } } else if (fromPlugin && fromPlugin.urlId !== urlId) { // 当存在同一组件的promise时需要判断条件是否相同 // 条件不同时取消前一个 if (cacheAfter.updateType && (cacheAfter.updateType === 'base' || /^layer\d*/.test(cacheAfter.updateType))) { // svgmap 不中止 } else { fromPlugin.abort(); } fromUrl = this.data(urlIdPromise); // 不需要查找是否存在新的相同的 data,这个在上边已经处理 // 需要查找是否存在新的相同的promise缓存 if (fromUrl && !fromUrl.isDestroy) { // 存在新的相同的promise缓存时通过队列建立返回对象 promise = this._createPlugins(fromUrl, pluginId); } else { // 不存在时需要新建队列以及promise返回对象 promise = this._createUrlList({ param: { url, type, data, dataType }, expire, cacheAfter, urlIdPromise, pluginId, urlId, successFlag }); } } else if (fromPlugin && fromPlugin.urlId === urlId) { // 当存在同一组件的promise时需要判断条件是否相同 // 条件相同时返回一个假promise promise = this._createPlugins(); } else if (!fromPlugin && fromUrl) { // 不存在组件promise缓存时,且存在urlPromise队列时 添加一个新的promise给该队列 promise = this._createPlugins(fromUrl, pluginId, urlId); } else if (!fromPlugin && !fromUrl) { // 不存在组件promise缓存时,且不存在urlPromise队列时 添加一个新的promise给该队列 promise = this._createUrlList({ param: { url, type, data, dataType }, expire, cacheAfter, urlIdPromise, pluginId, urlId, successFlag }); } // eslint-disable-next-line consistent-return /* _cache1.push(promise); */ return promise; }, /** * @Author TangChengchuan * @Date 2020/3/3 * @Description 建立以url为id的缓存队列 * @param paramList [Object] => ajax请求参数 * @return Promise [Promise] => $.ajax代理对象 */ _createUrlList(paramList) { // 获取缓存时间参数 const { param, expire, cacheAfter, urlIdPromise, pluginId, urlId, successFlag } = paramList; // 以便在network中查看组件对应的query请求。同时为了运用缓存,url拼接的pluginId和pluginName只能在发送前拼接 param.url += `?pluginid=${cacheAfter.pluginid}&pluginName=${cacheAfter.pluginName}&pluginType=${cacheAfter.updateType}`; // 拼接请求条件 param.data = $.extend(true, {}, param.data, cacheAfter); // 生成真实ajax; const realAjax = $.ajax(param); // 生成urlPromise const urlPromise = Object.create(this); Object.assign(urlPromise, { // 缓存id urlIdPromise, // ajax realAjax, // 真实缓存队列 _cache: [], /** * @Author TangChengchuan * @Date 2020/3/3 * @Description 添加plugin缓存 * @param pluginPromise [Promise] => 组件缓存代理 */ add(pluginPromise) { this._cache.push(pluginPromise); }, /** * @Author TangChengchuan * @Date 2020/3/3 * @Description 删除plugin缓存 * @param pluginPromise [Promise] => 组件缓存代理 */ remove(pluginPromise) { this._cache = this._cache.filter( (itemPromise) => itemPromise !== pluginPromise ); if (!this._cache.length) { // 当不存在队列时需要销毁 // 注销缓存 this.data(this.urlIdPromise, null); this.realAjax.abort(); this.isDestroy = true; } }, /** * @Author TangChengchuan * @Date 2020/3/3 * @Description 返回是否存在缓存 * @param pluginPromise [Promise] => 组件缓存代理 */ check(pluginPromise) { return this._cache.some( (itemPromise) => itemPromise === pluginPromise ); }, /** * @Author TangChengchuan * @Date 2020/3/3 * @Description 执行队列 */ execute(res, flag) { // 注销urlPromise缓存 this.data(this.urlIdPromise, null); // 加入数据缓存 this.data(urlId, null); // 由于query条件不含success和successFlag,这里永远进不去,暂时保留看后续是否需要调整 -sf2368 // modify by lmf 2020.4.28添加flag为true时进入条件逻辑, // 解决初始化联动svg地图时,取消请求会走this.data(urlId, res);设置urlId的缓存 if (res && (res.success || res[successFlag]) && flag) { this.data(urlId, res); if (_.isNumber(expire)) { setTimeout(() => { this.data(urlId, null); }, expire); } } // 执行所有数据 this._cache.forEach((pluginPromise) => { // 注销pluginPromise缓存 this.data(pluginPromise.id, null); try { pluginPromise.execute(res, flag); } catch (e) { // eslint-disable-next-line no-console console.warn(e); } }); }, abort() { this._cache.forEach((item) => item.abort()); } }); realAjax.success((res) => { urlPromise.execute(res, true); }).error((res) => { urlPromise.execute(res, false); }); // 生成pluginPromise return this._createPlugins(urlPromise, pluginId, urlId); }, _createPlugins(urlPromise, pluginId, urlId) { if (urlPromise) { // // 注册缓存 // this.data(urlIdPromise, urlPromise); const _realPromise = Promise.createPromise(); const pluginPromise = Object.create(_realPromise); Object.assign(pluginPromise, { id: pluginId, urlId, _realPromise, urlPromise, success(...arg) { this.check(); return this._realPromise.success(...arg); }, then(...arg) { this.check(); return this._realPromise.then(...arg); }, error(...arg) { this.check(); return this._realPromise.error(...arg); }, fail(...arg) { this.check(); return this._realPromise.fail(...arg); }, data: this.data, check() { const promise = this.data(this.urlPromise.urlIdPromise); if (promise && promise !== this.urlPromise) { this.urlPromise.abort(); this.urlPromise = promise; } else if (!promise) { this.data(this.urlPromise.urlIdPromise, this.urlPromise); } if (!this.urlPromise.check(this)) { this.data(this.id, this); this.urlPromise.add(this); } }, execute(...arg) { this.abort(); return this._realPromise.execute(...arg); }, abort() { this.data(this.id, null); this.urlPromise.remove(this); } }); return pluginPromise; } return { id: pluginId, success() { return this; }, then() { return this; }, abort() { return this; }, error() { return this; }, fail() { return this; }, execute() { return this; } }; } }; return ajax; });