原生JS發(fā)送異步數據請求
在做項目的時候,有時候需要用到異步數據請求,但是如果這個時候沒有框架的依賴,就需要用到原生JS進行異步數據請求。這時候無非有兩種請求方式,一種是AJAX,另一個是JSONP。通過原生JS對異步請求進行簡單的封裝。
AJAX
AJAX是一種數據請求方式,不需要刷新整個頁面就能夠更新局部頁面的數據。AJAX的技術核心是XMLHttpRequest對象,主要請求過程如下:
- 創(chuàng)建XMLHttpRequest對象(new)
- 連接服務器(open)
- 發(fā)送請求(send)
- 接收響應數據(onreadystatechange)
不說話直接貼代碼
/** * 通過JSON的方式請求 * @param {[type]} params [description] * @return {[type]} [description] */ ajaxJSON(params) { params.type = (params.type || 'GET').toUpperCase(); params.data = params.data || {}; var formatedParams = this.formateParams(params.data, params.cache); var xhr; //創(chuàng)建XMLHttpRequest對象 if (window.XMLHttpRequest) { //非IE6 xhr = new XMLHttpRequest(); } else { xhr = new ActiveXObject('Microsoft.XMLHTTP'); } //異步狀態(tài)發(fā)生改變,接收響應數據 xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status == 200) { if (!!params.success) { if (typeof xhr.responseText == 'string') { params.success(JSON.parse(xhr.responseText)); } else { params.success(xhr.responseText); } } } else { params.error && params.error(status); } } if (params.type == 'GET') { //連接服務器 xhr.open('GET', (!!formatedParams ? params.url + '?' + formatedParams : params.url), true); //發(fā)送請求 xhr.send(); } else { //連接服務器 xhr.open('POST', params.url, true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); //發(fā)送請求 xhr.send(formatedParams); } }, /** * 格式化數據 * @param {Obj} data 需要格式化的數據 * @param {Boolean} isCache 是否加入隨機參數 * @return {String} 返回的字符串 */ formateParams: function(data, isCache) { var arr = []; for (var name in data) { arr.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name])); } if (isCache) { arr.push('v=' + (new Date()).getTime()); } return arr.join('&'); }
IE7及其以上版本中支持原生的 XHR 對象,因此可以直接用:var oAjax = new XMLHttpRequest();。IE6及其之前的版本中,XHR對象是通過MSXML庫中的一個ActiveXObject對象實現的。
通過xhr的open函數來連接服務器,主要接收三個參數:請求方式、請求地址和是否異步請求(一般都是異步請求)。請求方式有兩種,GET和POST,GET是通過URL將數據提交到服務器的,POST則是通過將數據作為send方法的參數發(fā)送到服務器。
給xhr綁定狀態(tài)改變函數onreadystatechange,主要用來檢測xhr的readyState的變化,當異步發(fā)送成功后,readyState的數值會由0變成4,同時觸發(fā)onreadystatechange事件。readyState的屬性及對應狀態(tài)如下:
0 (未初始化) 對象已建立,但是尚未初始化(尚未調用open方法)
1 (初始化) 對象已建立,尚未調用send方法
2 (發(fā)送數據) send方法已調用,但是當前的狀態(tài)及http頭未知
3 (數據傳送中) 已接收部分數據,因為響應及http頭不全,這時通過responseBody和responseText獲取部分數據會出現錯誤
4 (完成) 數據接收完畢,此時可以通過通過responseBody和responseText獲取完整的回應數據
在readystatechange事件中,先判斷響應是否接收完成,然后判斷服務器是否成功處理請求,xhr.status 是狀態(tài)碼,狀態(tài)碼以2開頭的都是成功,304表示從緩存中獲取,上面的代碼在每次請求的時候都加入了隨機數,所以不會從緩存中取值,故該狀態(tài)不需判斷。
JSONP
如果還是用上面的XMLHttpRequest對象來發(fā)送需要跨域的請求,雖然調用了send函數,但是xhr的狀態(tài)一直都是0,也不會觸發(fā)onreadystatechange事件,這個時候就要用到JSONP的請求方式了。
JSONP(JSON with Padding) 是一種跨域請求方式。主要原理是利用了script標簽可以跨域請求的特點,由其src屬性發(fā)送請求到服務器,服務器返回js代碼,網頁端接受響應,然后就直接執(zhí)行了,這和通過script標簽引用外部文件的原理是一樣的。
JSONP由兩部分組成:回調函數和數據,回調函數一般是由網頁端控制,作為參數發(fā)往服務器端,服務器端把該函數和數據拼成字符串返回。
比如網頁端創(chuàng)建一個script標簽,并給其src賦值為http://www.test.com/json/?callback=process, 此時網頁端就發(fā)起一個請求。服務端將要返回的數據拼作為函數的參數傳入,服務端返回的數據格式類似”process({‘name:'xieyufei'})”,網頁端接收到了響應值,因為請求者是 script,所以相當于直接調用process方法,并且傳入了一個參數。
不說話直接貼代碼。
/** * 通過JSONP的方式請求 * @param {[type]} params [description] * @return {[type]} [description] */ ajaxJSONP(params) { params.data = params.data || {}; params.jsonp = params.jsonp || 'callback'; // 設置傳遞給后臺的回調參數名和參數值 var callbackName = 'jsonp_' + (new Date()).getTime(); params.data[params.jsonp] = callbackName; var formatedParams = this.formateParams(params.data, params.cache); //創(chuàng)建script標簽并插入到頁面中 var head = document.getElementsByTagName('head')[0]; var script = document.createElement('script'); head.appendChild(script); //創(chuàng)建jsonp回調函數 window[callbackName] = function(json) { head.removeChild(script); clearTimeout(script.timer); window[callbackName] = null; params.success && params.success(json); }; //發(fā)送請求 script.src = (!!formatedParams ? params.url + '?' + formatedParams : params.url); //為了得知此次請求是否成功,設置超時處理 if (params.time) { script.timer = setTimeout(function() { window[callbackName] = null; head.removeChild(script); params.error && params.error({ message: '超時' }); }, params.time); } }
給script標簽設置src屬性時瀏覽器就會去發(fā)送請求,但是只能發(fā)送一次請求,導致script標簽不能復用,因此每次操作完都需要把script標簽移除。在瀏覽器發(fā)送請求之前給全局綁定一個回調函數,當數據請求成功時就會調用這個回調函數。
總結
將兩種發(fā)送異步數據的方式整合起來,根據dataType來進行判斷選用哪種方式。貼上完整的代碼
var xyfAjax = { ajax: function(params) { params = params || {}; params.cache = params.cache || false; if (!params.url) { throw new Error('參數不合法'); } params.dataType = (params.dataType || 'json').toLowerCase(); if (params.dataType == 'jsonp') { this.ajaxJSONP(params); } else if (params.dataType == 'json') { this.ajaxJSON(params); } }, /** * 通過JSONP的方式請求 * @param {[type]} params [description] * @return {[type]} [description] */ ajaxJSONP(params) { params.data = params.data || {}; params.jsonp = params.jsonp || 'callback'; // 設置傳遞給后臺的回調參數名和參數值 var callbackName = 'jsonp_' + (new Date()).getTime(); params.data[params.jsonp] = callbackName; var formatedParams = this.formateParams(params.data, params.cache); //創(chuàng)建script標簽并插入到頁面中 var head = document.getElementsByTagName('head')[0]; var script = document.createElement('script'); head.appendChild(script); //創(chuàng)建jsonp回調函數 window[callbackName] = function(json) { head.removeChild(script); clearTimeout(script.timer); window[callbackName] = null; params.success && params.success(json); }; //發(fā)送請求 script.src = (!!formatedParams ? params.url + '?' + formatedParams : params.url); //為了得知此次請求是否成功,設置超時處理 if (params.time) { script.timer = setTimeout(function() { window[callbackName] = null; head.removeChild(script); params.error && params.error({ message: '超時' }); }, params.time); } }, /** * 通過JSON的方式請求 * @param {[type]} params [description] * @return {[type]} [description] */ ajaxJSON(params) { params.type = (params.type || 'GET').toUpperCase(); params.data = params.data || {}; var formatedParams = this.formateParams(params.data, params.cache); var xhr; //創(chuàng)建XMLHttpRequest對象 if (window.XMLHttpRequest) { //非IE6 xhr = new XMLHttpRequest(); } else { xhr = new ActiveXObject('Microsoft.XMLHTTP'); } //異步狀態(tài)發(fā)生改變,接收響應數據 xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status == 200) { if (!!params.success) { if (typeof xhr.responseText == 'string') { params.success(JSON.parse(xhr.responseText)); } else { params.success(xhr.responseText); } } } else { params.error && params.error(status); } } if (params.type == 'GET') { //連接服務器 xhr.open('GET', (!!formatedParams ? params.url + '?' + formatedParams : params.url), true); //發(fā)送請求 xhr.send(null); } else { //連接服務器 xhr.open('POST', params.url, true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); //發(fā)送請求 xhr.send(formatedParams); } }, /** * 格式化數據 * @param {Obj} data 需要格式化的數據 * @param {Boolean} isCache 是否加入隨機參數 * @return {String} 返回的字符串 */ formateParams: function(data, isCache) { var arr = []; for (var name in data) { arr.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name])); } if (isCache) { arr.push('v=' + (new Date()).getTime()); } return arr.join('&'); } } xyfAjax.ajax({ url:'http://www.xieyufei.com', type:'get', //or post dataType:'json', //or jsonp data:{ name:'xyf' }, success: function(data){ console.log(data) } })
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
完美解決spring websocket自動斷開連接再創(chuàng)建引發(fā)的問題
下面小編就為大家?guī)硪黄昝澜鉀Qspring websocket自動斷開連接再創(chuàng)建引發(fā)的問題。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-03-03JavaScript生成器函數Generator解決異步操作問題
這篇文章主要為大家介紹了JavaScript生成器函數Generator解決異步操作問題示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10