原生JS發(fā)送異步數(shù)據(jù)請(qǐng)求
在做項(xiàng)目的時(shí)候,有時(shí)候需要用到異步數(shù)據(jù)請(qǐng)求,但是如果這個(gè)時(shí)候沒有框架的依賴,就需要用到原生JS進(jìn)行異步數(shù)據(jù)請(qǐng)求。這時(shí)候無(wú)非有兩種請(qǐng)求方式,一種是AJAX,另一個(gè)是JSONP。通過(guò)原生JS對(duì)異步請(qǐng)求進(jìn)行簡(jiǎn)單的封裝。
AJAX
AJAX是一種數(shù)據(jù)請(qǐng)求方式,不需要刷新整個(gè)頁(yè)面就能夠更新局部頁(yè)面的數(shù)據(jù)。AJAX的技術(shù)核心是XMLHttpRequest對(duì)象,主要請(qǐng)求過(guò)程如下:
- 創(chuàng)建XMLHttpRequest對(duì)象(new)
- 連接服務(wù)器(open)
- 發(fā)送請(qǐng)求(send)
- 接收響應(yīng)數(shù)據(jù)(onreadystatechange)
不說(shuō)話直接貼代碼
/** * 通過(guò)JSON的方式請(qǐng)求 * @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對(duì)象 if (window.XMLHttpRequest) { //非IE6 xhr = new XMLHttpRequest(); } else { xhr = new ActiveXObject('Microsoft.XMLHTTP'); } //異步狀態(tài)發(fā)生改變,接收響應(yīng)數(shù)據(jù) 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') { //連接服務(wù)器 xhr.open('GET', (!!formatedParams ? params.url + '?' + formatedParams : params.url), true); //發(fā)送請(qǐng)求 xhr.send(); } else { //連接服務(wù)器 xhr.open('POST', params.url, true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); //發(fā)送請(qǐng)求 xhr.send(formatedParams); } }, /** * 格式化數(shù)據(jù) * @param {Obj} data 需要格式化的數(shù)據(jù) * @param {Boolean} isCache 是否加入隨機(jī)參數(shù) * @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 對(duì)象,因此可以直接用:var oAjax = new XMLHttpRequest();。IE6及其之前的版本中,XHR對(duì)象是通過(guò)MSXML庫(kù)中的一個(gè)ActiveXObject對(duì)象實(shí)現(xiàn)的。
通過(guò)xhr的open函數(shù)來(lái)連接服務(wù)器,主要接收三個(gè)參數(shù):請(qǐng)求方式、請(qǐng)求地址和是否異步請(qǐng)求(一般都是異步請(qǐng)求)。請(qǐng)求方式有兩種,GET和POST,GET是通過(guò)URL將數(shù)據(jù)提交到服務(wù)器的,POST則是通過(guò)將數(shù)據(jù)作為send方法的參數(shù)發(fā)送到服務(wù)器。
給xhr綁定狀態(tài)改變函數(shù)onreadystatechange,主要用來(lái)檢測(cè)xhr的readyState的變化,當(dāng)異步發(fā)送成功后,readyState的數(shù)值會(huì)由0變成4,同時(shí)觸發(fā)onreadystatechange事件。readyState的屬性及對(duì)應(yīng)狀態(tài)如下:
0 (未初始化) 對(duì)象已建立,但是尚未初始化(尚未調(diào)用open方法)
1 (初始化) 對(duì)象已建立,尚未調(diào)用send方法
2 (發(fā)送數(shù)據(jù)) send方法已調(diào)用,但是當(dāng)前的狀態(tài)及http頭未知
3 (數(shù)據(jù)傳送中) 已接收部分?jǐn)?shù)據(jù),因?yàn)轫憫?yīng)及http頭不全,這時(shí)通過(guò)responseBody和responseText獲取部分?jǐn)?shù)據(jù)會(huì)出現(xiàn)錯(cuò)誤
4 (完成) 數(shù)據(jù)接收完畢,此時(shí)可以通過(guò)通過(guò)responseBody和responseText獲取完整的回應(yīng)數(shù)據(jù)
在readystatechange事件中,先判斷響應(yīng)是否接收完成,然后判斷服務(wù)器是否成功處理請(qǐng)求,xhr.status 是狀態(tài)碼,狀態(tài)碼以2開頭的都是成功,304表示從緩存中獲取,上面的代碼在每次請(qǐng)求的時(shí)候都加入了隨機(jī)數(shù),所以不會(huì)從緩存中取值,故該狀態(tài)不需判斷。
JSONP
如果還是用上面的XMLHttpRequest對(duì)象來(lái)發(fā)送需要跨域的請(qǐng)求,雖然調(diào)用了send函數(shù),但是xhr的狀態(tài)一直都是0,也不會(huì)觸發(fā)onreadystatechange事件,這個(gè)時(shí)候就要用到JSONP的請(qǐng)求方式了。
JSONP(JSON with Padding) 是一種跨域請(qǐng)求方式。主要原理是利用了script標(biāo)簽可以跨域請(qǐng)求的特點(diǎn),由其src屬性發(fā)送請(qǐng)求到服務(wù)器,服務(wù)器返回js代碼,網(wǎng)頁(yè)端接受響應(yīng),然后就直接執(zhí)行了,這和通過(guò)script標(biāo)簽引用外部文件的原理是一樣的。
JSONP由兩部分組成:回調(diào)函數(shù)和數(shù)據(jù),回調(diào)函數(shù)一般是由網(wǎng)頁(yè)端控制,作為參數(shù)發(fā)往服務(wù)器端,服務(wù)器端把該函數(shù)和數(shù)據(jù)拼成字符串返回。
比如網(wǎng)頁(yè)端創(chuàng)建一個(gè)script標(biāo)簽,并給其src賦值為http://www.test.com/json/?callback=process, 此時(shí)網(wǎng)頁(yè)端就發(fā)起一個(gè)請(qǐng)求。服務(wù)端將要返回的數(shù)據(jù)拼作為函數(shù)的參數(shù)傳入,服務(wù)端返回的數(shù)據(jù)格式類似”process({‘name:'xieyufei'})”,網(wǎng)頁(yè)端接收到了響應(yīng)值,因?yàn)檎?qǐng)求者是 script,所以相當(dāng)于直接調(diào)用process方法,并且傳入了一個(gè)參數(shù)。
不說(shuō)話直接貼代碼。
/** * 通過(guò)JSONP的方式請(qǐng)求 * @param {[type]} params [description] * @return {[type]} [description] */ ajaxJSONP(params) { params.data = params.data || {}; params.jsonp = params.jsonp || 'callback'; // 設(shè)置傳遞給后臺(tái)的回調(diào)參數(shù)名和參數(shù)值 var callbackName = 'jsonp_' + (new Date()).getTime(); params.data[params.jsonp] = callbackName; var formatedParams = this.formateParams(params.data, params.cache); //創(chuàng)建script標(biāo)簽并插入到頁(yè)面中 var head = document.getElementsByTagName('head')[0]; var script = document.createElement('script'); head.appendChild(script); //創(chuàng)建jsonp回調(diào)函數(shù) window[callbackName] = function(json) { head.removeChild(script); clearTimeout(script.timer); window[callbackName] = null; params.success && params.success(json); }; //發(fā)送請(qǐng)求 script.src = (!!formatedParams ? params.url + '?' + formatedParams : params.url); //為了得知此次請(qǐng)求是否成功,設(shè)置超時(shí)處理 if (params.time) { script.timer = setTimeout(function() { window[callbackName] = null; head.removeChild(script); params.error && params.error({ message: '超時(shí)' }); }, params.time); } }
給script標(biāo)簽設(shè)置src屬性時(shí)瀏覽器就會(huì)去發(fā)送請(qǐng)求,但是只能發(fā)送一次請(qǐng)求,導(dǎo)致script標(biāo)簽不能復(fù)用,因此每次操作完都需要把script標(biāo)簽移除。在瀏覽器發(fā)送請(qǐng)求之前給全局綁定一個(gè)回調(diào)函數(shù),當(dāng)數(shù)據(jù)請(qǐng)求成功時(shí)就會(huì)調(diào)用這個(gè)回調(diào)函數(shù)。
總結(jié)
將兩種發(fā)送異步數(shù)據(jù)的方式整合起來(lái),根據(jù)dataType來(lái)進(jìn)行判斷選用哪種方式。貼上完整的代碼
var xyfAjax = { ajax: function(params) { params = params || {}; params.cache = params.cache || false; if (!params.url) { throw new Error('參數(shù)不合法'); } params.dataType = (params.dataType || 'json').toLowerCase(); if (params.dataType == 'jsonp') { this.ajaxJSONP(params); } else if (params.dataType == 'json') { this.ajaxJSON(params); } }, /** * 通過(guò)JSONP的方式請(qǐng)求 * @param {[type]} params [description] * @return {[type]} [description] */ ajaxJSONP(params) { params.data = params.data || {}; params.jsonp = params.jsonp || 'callback'; // 設(shè)置傳遞給后臺(tái)的回調(diào)參數(shù)名和參數(shù)值 var callbackName = 'jsonp_' + (new Date()).getTime(); params.data[params.jsonp] = callbackName; var formatedParams = this.formateParams(params.data, params.cache); //創(chuàng)建script標(biāo)簽并插入到頁(yè)面中 var head = document.getElementsByTagName('head')[0]; var script = document.createElement('script'); head.appendChild(script); //創(chuàng)建jsonp回調(diào)函數(shù) window[callbackName] = function(json) { head.removeChild(script); clearTimeout(script.timer); window[callbackName] = null; params.success && params.success(json); }; //發(fā)送請(qǐng)求 script.src = (!!formatedParams ? params.url + '?' + formatedParams : params.url); //為了得知此次請(qǐng)求是否成功,設(shè)置超時(shí)處理 if (params.time) { script.timer = setTimeout(function() { window[callbackName] = null; head.removeChild(script); params.error && params.error({ message: '超時(shí)' }); }, params.time); } }, /** * 通過(guò)JSON的方式請(qǐng)求 * @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對(duì)象 if (window.XMLHttpRequest) { //非IE6 xhr = new XMLHttpRequest(); } else { xhr = new ActiveXObject('Microsoft.XMLHTTP'); } //異步狀態(tài)發(fā)生改變,接收響應(yīng)數(shù)據(jù) 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') { //連接服務(wù)器 xhr.open('GET', (!!formatedParams ? params.url + '?' + formatedParams : params.url), true); //發(fā)送請(qǐng)求 xhr.send(null); } else { //連接服務(wù)器 xhr.open('POST', params.url, true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); //發(fā)送請(qǐng)求 xhr.send(formatedParams); } }, /** * 格式化數(shù)據(jù) * @param {Obj} data 需要格式化的數(shù)據(jù) * @param {Boolean} isCache 是否加入隨機(jī)參數(shù) * @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) } })
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Node.js模擬發(fā)起http請(qǐng)求從異步轉(zhuǎn)同步的5種用法
- 原生JavaScript實(shí)現(xiàn)Ajax異步請(qǐng)求
- JavaScript利用fetch實(shí)現(xiàn)異步請(qǐng)求的方法實(shí)例
- 詳解Vuejs2.0之異步跨域請(qǐng)求
- SpringMVC環(huán)境下實(shí)現(xiàn)的Ajax異步請(qǐng)求JSON格式數(shù)據(jù)
- angularjs 處理多個(gè)異步請(qǐng)求方法匯總
- jquery的ajax異步請(qǐng)求接收返回json數(shù)據(jù)實(shí)例
- JS多個(gè)異步請(qǐng)求 按順序執(zhí)行next實(shí)現(xiàn)解析
相關(guān)文章
完美解決spring websocket自動(dòng)斷開連接再創(chuàng)建引發(fā)的問(wèn)題
下面小編就為大家?guī)?lái)一篇完美解決spring websocket自動(dòng)斷開連接再創(chuàng)建引發(fā)的問(wèn)題。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-03-03基于JavaScript實(shí)現(xiàn)的順序查找算法示例
這篇文章主要介紹了基于JavaScript實(shí)現(xiàn)的順序查找算法,結(jié)合實(shí)例形式分析了javascript順序查找的原理、實(shí)現(xiàn)技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-04-04PHP抓取HTTPS內(nèi)容和錯(cuò)誤處理的方法
這篇文章主要介紹了PHP抓取HTTPS內(nèi)容的實(shí)現(xiàn)方法,以及在抓取的時(shí)候遇到的一個(gè)HTTPS問(wèn)題的處理辦法,有需要的朋友們可以參考借鑒,下面來(lái)一起看看吧。2016-09-09JavaScript生成器函數(shù)Generator解決異步操作問(wèn)題
這篇文章主要為大家介紹了JavaScript生成器函數(shù)Generator解決異步操作問(wèn)題示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10JS實(shí)現(xiàn)探測(cè)網(wǎng)站鏈接的方法【測(cè)試可用】
這篇文章主要介紹了JS實(shí)現(xiàn)探測(cè)網(wǎng)站鏈接的方法,通過(guò)網(wǎng)站返回錯(cuò)誤響應(yīng)觸發(fā)onerror時(shí)間來(lái)判斷網(wǎng)站鏈接的可用性,非常簡(jiǎn)便實(shí)用,需要的朋友可以參考下2016-11-11使用JavaScript實(shí)現(xiàn)輪播圖特效
這篇文章主要為大家詳細(xì)介紹了使用JavaScript實(shí)現(xiàn)輪播圖特效,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09