[JS] 原生Ajax函数

2018-07-05  本文已影响0人  w_w_wei
/**
 * ajax和跨域jsonp
 * Created by xgx on 2015-10-08.
 *
 * @param {string} url
 * @param {Object} options
 *          {string} type 请求方式
 *              default: GET
 *              GET | POST
 *          {string | Object} data 数据
 *              可以是对象或构造好的字符串
 *          {bool} crossDomain 是否跨域
 *              跨域则使用jsonp异步方式
 *          {int} timeout  超时时间
 *              单位ms
 *              default: 60 000
 *          {Function} error 失败回调
 *          {Function} success 成功回调
 *          {Function} resultFilter 结果过滤
 *              根据结果来判断成功失败, 用来处理通用的已知错误, 仅支持jsonp
 *          {Function} complete 请求完成后回调
 *              无论成功失败均会执行
 *          {string} jsonpCallback
 *              default: EA_cb + 自增值
 *          {string} jsonp  回调函数字段名
 *              default: callback,
 *          {bool} async 是否异步
 *              默认为异步
 *              default: true, force true for crossDomain 'true'
 *          {bool} cache 缓存
 *              false仅在type 'GET' 时有效
 *              default: true
 *          {string} dataType 数据类型
 *              json时会自动解析, script时会自动执行
 *              default: xml,
 *              xml | json | script
 */
function ajax(url, options) {
    //异步请求回调函数序号,从10000开始自增
    this.jsonpIndex = this.jsonpIndex || 10000;
    options = options || {};
    options = {
        type: options.type || "GET",
        data: options.data || "",
        complete: options.complete || new Function(),
        error: options.error || new Function(),
        success: options.success || new Function(),
        resultFilter: options.resultFilter || new Function(),
        //默认1min超时
        timeout: options.timeout || 1000 * 60,

        //jsonp
        crossDomain: options.crossDomain || false,
        jsonpCallback: options.jsonpCallback || "EA_cb" + this.jsonpIndex,
        jsonp: options.jsonp || "callback",

        //默认为异步
        async: typeof(options.async) === "undefined" ? true : options.async,
        cache: typeof(options.cache) === "undefined" ? true : options.cache,
        dataType: options.dataType || ""
    };

    //处理data
    var dataString = "";
    if (typeof (options.data) === "object") {
        for (var x in options.data) {
            if (options.data.hasOwnProperty(x)) {
                dataString += "&" + x + "=" + encodeURIComponent(options.data[x]);
            }
        }
        options.data = dataString.substr(1);
    }
    //GET & JSONP 都通过URL传data
    if (options.data && (options.type === "GET" || options.crossDomain)) {
        url += (url.indexOf("?") !== -1) ? "&" : "?";
        url += options.data;
    }

    //处理跨域请求
    if (options.crossDomain) {
        this.jsonpIndex++;
        return jsonp(url, options);
    }

    //处理缓存
    if (!options.cache && options.type === "GET") {
        url += (url.indexOf("?") !== -1) ? "&" : "?";
        url += "_=" + Math.random().toString().substr(2);
    }

    if (typeof XMLHttpRequest === "undefined") {
        XMLHttpRequest = function () {
            var xmlhttp;
            if (window.ActiveXObject) {
                xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
                if (!xmlhttp) {
                    xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
                }
            } else {
                alert("Init XMLHTTP Error!");
            }
            return xmlhttp;
        }
    }

    var xml = new XMLHttpRequest();
    xml.open(options.type, url, options.async);

    var requestDone = false;
    window.setTimeout(function () {
        if (requestDone) {
            return;
        }
        requestDone = true;
        options.error();
    }, options.timeout);

    //监听文档状态的改变
    xml.onreadystatechange = function () {
        if (xml.readyState == 4 && !requestDone) {
            requestDone = true;
            if (httpSuccess(xml)) {
                options.success(httpData(xml, options.dataType));
            }
            else {
                options.error(xml, xml.status, xml.statusText);
            }
            options.complete();
            //为避免内存泄漏,清理文档
            xml = null;
        }
    };

    //header
    xml.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

    try {
        if (options.data) {
            xml.send(options.data);
        }
        else {
            xml.send(null);
        }
    }
    catch (e) {
        return null;
    }

    //JSONP跨域请求
    function jsonp(url, options) {
        var timeout_trigger = window.setTimeout(function () {
            window[options.jsonpCallback] = new Function();
            console.error("JSONP: Connect timeout: ", options.timeout);
            options.error();
            //清空,防止超时和出错时重复调用
            options.error = new Function();
            options.complete();
            options.complete = new Function();
            removeDom(options.jsonpCallback);
        }, options.timeout);
        var json;
        url += (url.indexOf("?") !== -1) ? "&" : "?";
        url += options.jsonp + "=" + options.jsonpCallback;
        window[options.jsonpCallback] = function (data) {
            window.clearTimeout(timeout_trigger);
            options.complete();
            if (options.dataType == "json") {
                if (!data.length) {
                    console.error("JSONP: Json data is Empty , On URL : ", url);
                    options.error();
                    return;
                }
                try {
                    json = JSON.parse(data);
                } catch (e) {
                    //json解析异常
                    console.error(
                        "JSONP: Json data parse error : ", e, ", data is : ", data, ", On URL : ", url
                    );
                    options.error();
                    return;
                }
                if (options.resultFilter(json)) {
                    options.error();
                } else {
                    options.success(json);
                }
            } else {
                options.success(data);
            }
            removeDom(options.jsonpCallback);
        };
        var script = document.createElement("script");
        script.id = options.jsonpCallback;
        script.type = "text/javascript";
        script.async = true;
        script.onerror = function () {
            console.error("JSONP: Network Connect error!");
            window.clearTimeout(timeout_trigger);
            options.error();
            options.error = new Function();
            removeDom(options.jsonpCallback);
        };
        script.src = url;
        document.getElementsByTagName("head")[0].appendChild(script);
    }

    //删除指定Script标签
    function removeDom(id) {
        setTimeout(function () {
            var dom = $ID(id);
            if (dom) {
                document.getElementsByTagName("head")[0].removeChild(dom);
            }
            //30s 后再清除, 避免出现加载问题
        }, 30000);
    }

    //判断http响应是否成功
    function httpSuccess(r) {
        try {
            return (!r.status && location.protocol == "file:") ||
                (r.status >= 200 && r.status < 300) ||
                r.status == 304 ||
                (navigator.userAgent.indexOf("Safari") >= 0 &&
                r.status === undefined);
        }
        catch (e) {
        }
        return false;
    }

    //从http响应中解析得到正确的数据
    function httpData(r, type) {
        var ct = r.getResponseHeader("Content-Type");
        type = !type ? (ct.indexOf("xml") >= 0 ? "xml" : "") : type;
        var data = type == "xml" ? r.responseXML : r.responseText;
        if (type == "script") {
            eval.call(window, data);
        }
        if (type == "json") {
            try {
                data = JSON.parse(data);

            } catch (e) {
                //json解析异常
                console.error(
                    "JSONP: Json data parse error : ", e, ", data is : ", data, ", On URL : ", url
                );
                options.error();
                return;
            }
        }
        return data;
    }
}
上一篇 下一篇

猜你喜欢

热点阅读