AlexZ33 / lessions

自己练习的各种demo和课程
12 stars 2 forks source link

ajax #102

Open AlexZ33 opened 3 years ago

AlexZ33 commented 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;
});