ajax 、jsonp 、Promise 封裝

一、JS原生Ajax

ajax:一種請求數據的方式,不需要刷新整個頁面;
ajax的技術核心是 XMLHttpRequest 對象;
ajax 請求過程:創建 XMLHttpRequest 對象、連接服務器、發送請求、接收響應數據;

下面簡單封裝一個函數,之後稍作解釋

 ajax({
        url: "./TestXHR.aspx",              //請求地址
        type: "POST",                       //請求方式
        data: { name: "super", age: 20 },        //請求參數
        dataType: "json",
        success: function (response, xml) {
            // 此處放成功後執行的代碼
        },
        fail: function (status) {
            // 此處放失敗後執行的代碼
        }
    });

    function ajax(options) {
        options = options || {};
        options.type = (options.type || "GET").toUpperCase();
        options.dataType = options.dataType || "json";
        var params = formatParams(options.data);

        //創建 - 非IE6 - 第一步
        if (window.XMLHttpRequest) {
            var xhr = new XMLHttpRequest();
        } else { //IE6及其以下版本瀏覽器
            var xhr = new ActiveXObject('Microsoft.XMLHTTP');
        }

        //接收 - 第三步
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) {
                var status = xhr.status;
                if (status >= 200 && status < 300) {
                    options.success && options.success(xhr.responseText, xhr.responseXML);
                } else {
                    options.fail && options.fail(status);
                }
            }
        }

        //連接 和 發送 - 第二步
        if (options.type == "GET") {
            xhr.open("GET", options.url + "?" + params, true);
            xhr.send(null);
        } else if (options.type == "POST") {
            xhr.open("POST", options.url, true);
            //設置表單提交時的內容類型
            xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            xhr.send(params);
        }
    }
    //格式化參數
    function formatParams(data) {
        var arr = [];
        for (var name in data) {
            arr.push(encodeURIComponent(name) + "=" + encodeURIComponent(data[name]));
        }
        arr.push(("v=" + Math.random()));
        return arr.join("&");
    }

1、創建

1.1、IE7及其以上版本中支持原生的 XHR 對象,因此可以直接用: var oAjax = new XMLHttpRequest();

1.2、IE6及其之前的版本中,XHR對象是通過MSXML庫中的一個ActiveX對象實現的。有的書中細化了IE中此類對象的三種不同版本,即MSXML2.XMLHttp、MSXML2.XMLHttp.3.0 和 MSXML2.XMLHttp.6.0;個人感覺太麻煩,可以直接使用下面的語句創建: var oAjax=new ActiveXObject(’Microsoft.XMLHTTP’);

2、連接和發送

2.1、open()函數的三個參數:請求方式、請求地址、是否異步請求(同步請求的情況極少,至今還沒用到過);

2.2、GET 請求方式是通過URL參數將數據提交到服務器的,POST則是通過將數據作爲 send 的參數提交到服務器;

2.3、POST 請求中,在發送數據之前,要設置表單提交的內容類型;

2.4、提交到服務器的參數必須經過 encodeURIComponent() 方法進行編碼,實際上在參數列表”key=value”的形式中,key 和 value 都需要進行編碼,因爲會包含特殊字符。每次請求的時候都會在參數列表中拼入一個 “v=xx” 的字符串,這樣是爲了拒絕緩存,每次都直接請求到服務器上。

encodeURI() :用於整個 URI 的編碼,不會對本身屬於 URI 的特殊字符進行編碼,如冒號、正斜槓、問號和井號;其對應的解碼函數 decodeURI();
encodeURIComponent() :用於對 URI 中的某一部分進行編碼,會對它發現的任何非標準字符進行編碼;其對應的解碼函數 decodeURIComponent();

3、接收

3.1、接收到響應後,響應的數據會自動填充XHR對象,相關屬性如下
responseText:響應返回的主體內容,爲字符串類型;
responseXML:如果響應的內容類型是 “text/xml” 或 “application/xml”,這個屬性中將保存着相應的xml 數據,是 XML 對應的 document 類型;
status:響應的HTTP狀態碼;
statusText:HTTP狀態的說明;

3.2、XHR對象的readyState屬性表示請求/響應過程的當前活動階段,這個屬性的值如下
0-未初始化,尚未調用open()方法;
1-啓動,調用了open()方法,未調用send()方法;
2-發送,已經調用了send()方法,未接收到響應;
3-接收,已經接收到部分響應數據;
4-完成,已經接收到全部響應數據;

只要 readyState 的值變化,就會調用 readystatechange 事件,(其實爲了邏輯上通順,可以把readystatechange放到send之後,因爲send時請求服務器,會進行網絡通信,需要時間,在send之後指定readystatechange事件處理程序也是可以的,我一般都是這樣用,但爲了規範和跨瀏覽器兼容性,還是在open之前進行指定吧)。

3.3、在readystatechange事件中,先判斷響應是否接收完成,然後判斷服務器是否成功處理請求,xhr.status 是狀態碼,狀態碼以2開頭的都是成功,304表示從緩存中獲取,上面的代碼在每次請求的時候都加入了隨機數,所以不會從緩存中取值,故該狀態不需判斷。

4、ajax請求是不能跨域的!

二、JSONP

  JSONP(JSON with Padding) 是一種跨域請求方式。主要原理是利用了script 標籤可以跨域請求的特點,由其 src 屬性發送請求到服務器,服務器返回 js 代碼,網頁端接受響應,然後就直接執行了,這和通過 script 標籤引用外部文件的原理是一樣的。

  JSONP由兩部分組成:回調函數和數據,回調函數一般是由網頁端控制,作爲參數發往服務器端,服務器端把該函數和數據拼成字符串返回。

  比如網頁端創建一個 script 標籤,並給其 src 賦值爲 http://localhost:8080/ac/test.php?callback=foo, 此時網頁端就發起一個請求。服務端將要返回的數據拼好最爲函數的參數傳入,服務端返回的數據格式類似foo({"name":"OK"}),網頁端接收到了響應值,因爲請求者是 script,所以相當於直接調用 process 方法,並且傳入了一個參數。

  單看響應返回的數據,JSONP 比 ajax 方式就多了一個回調函數。

jsonp({
    url: "http://localhost:8080/ac/test.php",              
    data: { q: 1 },
    callbackName: "foo",
    time:1,
    success: function (json) {
        // 此處放成功後執行的代碼
        console.log(json.name);

    },
    error: function(json){
        console.log(json.message);
    }  
});

function jsonp(options) {
    options = options || {};
    options.callback = options.callback || "callback";
    if (!options.url || !options.callbackName) {
        throw new Error("參數不合法");
    }

    //創建 script 標籤並加入到頁面中
    var callbackName = (options.callbackName + Math.random()).replace(".", "");
    options.data[options.callback] = callbackName;      

    var params = formatParams(options.data);

    var oHead = document.getElementsByTagName('head')[0];
    var oS = document.createElement('script');
    oHead.appendChild(oS);

    //創建jsonp回調函數
    callbackName = callbackName.split("0")[0];
    window[callbackName] = function (json) {
        oHead.removeChild(oS);
        clearTimeout(oS.timer);
        window[callbackName] = null;
        options.success && options.success(json);
    };

    //發送請求
    oS.src = options.url + '?' + params;

    //超時處理
    if (options.time) {
        oS.timer = setTimeout(function () {
            window[callbackName] = null;
            oHead.removeChild(oS);
            options.error && options.error({ message: "超時" });
        }, options.time);
    }
};

//格式化參數
function formatParams(data) {
    var arr = [];
    for (var name in data) {
        arr.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name]));
    }
    return arr.join('&');
}

//test.php
<?php
$callback = $_GET["callback"];
$callback = explode("0",$callback);
$callback = $callback[0];
$array = [
    "foo" => ["name" => "OK"],
    "aaa" => "bar"
];
$result = $array[$callback];
echo $callback.'('.json_encode($result).')';
exit;
?>

1、因爲 script 標籤的 src 屬性只在第一次設置的時候起作用,導致 script 標籤沒法重用,所以每次完成操作之後要移除;

2、JSONP這種請求方式中,參數依舊需要編碼;

3、如果不設置超時,就無法得知此次請求是成功還是失敗;

三、Promise

Promise是異步編程的一種解決方案,比傳統的解決方案——回調函數和事件——更合理和更強大。它由社區最早提出和實現,ES6將其寫進了語言標準,統一了用法,原生提供了Promise對象。

所謂Promise,簡單說就是一個容器,裏面保存着某個未來纔會結束的事件(通常是一個異步操作)的結果。從語法上說,Promise是一個對象,從它可以獲取異步操作的消息。Promise提供統一的API,各種異步操作都可以用同樣的方法進行處理。

var getJSON = function(url) {
  var promise = new Promise(function(resolve, reject){
    var client = new XMLHttpRequest();
    client.open("GET", url);
    client.onreadystatechange = handler;
    client.responseType = "json";
    client.setRequestHeader("Accept", "application/json");
    client.send();

    function handler() {
      if (this.readyState !== 4) {
        return;
      }
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
  });

  return promise;
};

getJSON("https://api.github.com/search/users?q=1").then(function(json) {
  console.log('Contents: ' + json.items[0].id);
}, function(error) {
  console.error('出錯了', error);
});
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章