AJAX 不是 JavaScript 的規範,它只是一個哥們「發明」的縮寫:Asynchronous JavaScript and XML,意思就是用 JavaScript 執行異步網絡請求。
屬性 | 描述 |
---|---|
onreadystatechange | 每當 readyState 屬性改變時,就會調用該函數。 |
readyState | 存有 XMLHttpRequest 的狀態。從 0 到 4 發生變化。 0: 請求未初始化 1: 服務器鏈接已創建,open()方法已調用,可是 send()方法未調用 2: 請求已鏈接 send()方法已調用,HTTP 請求已發送到 Web 服務器。未接收到相應 3: 請求處理中 4: 請求已完成,且響應已就緒 |
status | 200: "OK" 404: 未找到頁面 |
;(function() { var xmlhttp if (window.XMLHttpRequest) { // IE7+, Firefox, Chrome, Opera, Safari 瀏覽器執行代碼 xmlhttp = new XMLHttpRequest() } else { // IE6, IE5 瀏覽器執行代碼 xmlhttp = new ActiveXObject('Microsoft.XMLHTTP') } xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { console.log(xmlhttp.responseText) } } xmlhttp.open('GET', 'http://www.runoob.com/try/ajax/ajax_info.txt', true) xmlhttp.send() })()
瀏覽器的同源策略致使的。默認狀況下,JavaScript 在發送 AJAX 請求時,URL 的域名必須和當前頁面徹底一致
跨域請求javascript
在 JavaScript 的世界中,全部代碼都是單線程執行的。java
因爲這個「缺陷」,致使 JavaScript 的全部網絡操做,瀏覽器事件,都必須是異步執行。異步執行能夠用回調函數實現node
function callback() { console.log('Done') } console.log('before setTimeout()') setTimeout(callback, 1000) // 1秒鐘後調用callback函數 console.log('after setTimeout()')
鏈式寫法的好處在於,先統一執行 AJAX 邏輯,不關心如何處理結果,而後,根據結果是成功仍是失敗,在未來的某個時候調用 success 函數或 fail 函數。jquery
古人云:「君子一言既出;駟馬難追」,這種「承諾未來會執行」的對象在 JavaScript 中稱爲 Promise 對象。ios
// ajax函數將返回Promise對象: function ajax(method, url, data) { var request = new XMLHttpRequest() return new Promise(function(resolve, reject) { request.onreadystatechange = function() { if (request.readyState === 4) { if (request.status === 200) { resolve(request.responseText) } else { reject(request.status) } } } request.open(method, url) request.send(data) }) } var p = ajax('GET', '/api/categories') p.then(function(text) { // 若是AJAX成功,得到響應內容 }).catch(function(status) { // 若是AJAX失敗,得到響應代碼 })
;(function() { console.time('doIt') const time1 = 300 step1(time1) .then(time2 => step2(time2)) .then(time3 => step3(time3)) .then(result => { console.log(`result is ${result}`) console.timeEnd('doIt') }) })() function takeLongTime(n) { return new Promise(resolve => { setTimeout(() => resolve(n + 200), n) }) } function step1(n) { console.log(`step1 with ${n}`) return takeLongTime(n) } function step2(n) { console.log(`step2 with ${n}`) return takeLongTime(n) } function step3(n) { console.log(`step3 with ${n}`) return takeLongTime(n) }
兩個任務是能夠並行執行的,用 Promise.all()實現
var p1 = new Promise(function(resolve, reject) { setTimeout(resolve, 500, 'P1') }) var p2 = new Promise(function(resolve, reject) { setTimeout(resolve, 6000, 'P2') }) // 同時執行p1和p2,並在它們都完成後執行then: Promise.all([p1, p2]).then(function(results) { console.log(results) // 得到一個Array: ['P1', 'P2'] })
有些時候,多個異步任務是爲了容錯,只須要得到先返回的結果便可
var p1 = new Promise(function(resolve, reject) { setTimeout(resolve, 3000, 'P1') }) var p2 = new Promise(function(resolve, reject) { setTimeout(resolve, 6000, 'P2') }) // 同時執行p1和p2,其中一個完成後執行then: Promise.race([p1, p2]).then(function(results) { console.log(results) // // 'P1' })
await 操做符用於等待一個 Promise 對象。它只能在異步函數 async function 中使用
await 表達式會暫停當前 async function 的執行,等待 Promise 處理完成。若 Promise 正常處理(fulfilled),其回調的 resolve 函數參數做爲 await 表達式的值,繼續執行 async function。
若 Promise 處理異常(rejected),await 表達式會把 Promise 的異常緣由拋出。es6
另外,若是 await 操做符後的表達式的值不是一個 Promise,則返回該值自己。ajax
// 若是一個 Promise 被傳遞給一個 await 操做符,await 將等待 Promise 正常處理完成並返回其處理結果。 function resolveAfter2Seconds(x) { return new Promise(resolve => { setTimeout(() => { resolve(x) }, 2000) }) } async function f1() { var x = await resolveAfter2Seconds(10) console.log(x) // 10 } f1() // 若是 Promise 處理異常,則異常值被拋出。 async function f3() { try { var z = await Promise.reject(30) } catch (e) { console.log(e) // 30 } } f3()
async function 聲明用於定義一個返回 AsyncFunction 對象的異步函數。異步函數是指經過事件循環異步執行的函數,它會經過一個隱式的 Promise 返回其結果。
一個返回的 Promise 對象會以 async function 的返回值進行解析(resolved),或者以該函數拋出的異常進行回絕(rejected)。npm
async 函數中可能會有 await 表達式,這會使 async 函數暫停執行,等待 Promise 的結果出來,而後恢復 async 函數的執行並返回解析值(resolved)。json
function resolveAfter2Seconds() { return new Promise(resolve => { setTimeout(() => { resolve('resolved') }, 2000) }) } async function asyncCall() { console.log('calling') var result = await resolveAfter2Seconds() console.log(result) // expected output: 'resolved' } asyncCall()
Fetch 是一個現代的概念, 等同於 XMLHttpRequest。它提供了許多與 XMLHttpRequest 相同的功能,但被設計成更具可擴展性和高效性。
Fetch 是掛在在 window 下的axios
Fetch 的核心在於對 HTTP 接口的抽象,包括 Request,Response,Headers,Body,以及用於初始化異步請求的 global fetch。Fetch 還利用到了請求的異步特性——它是基於 Promise 的。
fetch('http://example.com/movies.json') .then(function(response) { return response.json() }) .then(function(myJson) { console.log(myJson) })
postData('http://example.com/answer', { answer: 42 }) .then(data => console.log(data)) // JSON from `response.json()` call .catch(error => console.error(error)) function postData(url, data) { // Default options are marked with * return fetch(url, { body: JSON.stringify(data), // must match 'Content-Type' header cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached credentials: 'same-origin', // include, same-origin, *omit headers: { 'user-agent': 'Mozilla/4.0 MDN Example', 'content-type': 'application/json' }, method: 'POST', // *GET, POST, PUT, DELETE, etc. mode: 'cors', // no-cors, cors, *same-origin redirect: 'follow', // manual, *follow, error referrer: 'no-referrer' // *client, no-referrer }).then(response => response.json()) // parses response to JSON }
axios 是一個基於 Promise 用於瀏覽器和 nodejs 的 HTTP 客戶端
axios .get('/user', { params: { ID: 12345 } }) .then(function(response) {}) .catch(function(error) {})
能夠經過將相關配置傳遞給 axios 來進行請求。
axios({ method: 'post', url: '/user/12345', data: { firstName: 'Fred', lastName: 'Flintstone' } })
axios.request(config)
axios.get(url [,config])
axios.delete(url [,config])
axios.head(url [,config])
axios.post(url [,data [,config]])
axios.put(url [,data [,config]])
axios.patch(url [,data [,config]])
{ //`url`是服務器連接,用來請求 url:'/user', //`method`是發起請求時的請求方法 method:`get`, //`baseURL`若是`url`不是絕對地址,那麼將會加在其前面。 //當axios使用相對地址時這個設置很是方便 //在其實例中的方法 baseURL:'http://some-domain.com/api/', //`transformRequest`容許請求的數據在傳到服務器以前進行轉化。 //這個只適用於`PUT`,`GET`,`PATCH`方法。 //數組中的最後一個函數必須返回一個字符串或者一個`ArrayBuffer`,或者`Stream`,`Buffer`實例,`ArrayBuffer`,`FormData` transformRequest:[function(data){ //依本身的需求對請求數據進行處理 return data; }], //`transformResponse`容許返回的數據傳入then/catch以前進行處理 transformResponse:[function(data){ //依須要對數據進行處理 return data; }], //`headers`是自定義的要被髮送的頭信息 headers:{'X-Requested-with':'XMLHttpRequest'}, //`params`是請求鏈接中的請求參數,必須是一個純對象,或者URLSearchParams對象 params:{ ID:12345 }, //`paramsSerializer`是一個可選的函數,是用來序列化參數 //例如:(https://ww.npmjs.com/package/qs,http://api.jquery.com/jquery.param/) paramsSerializer: function(params){ return Qs.stringify(params,{arrayFormat:'brackets'}) }, //`data`是請求體須要設置的數據 //只適用於應用的'PUT','POST','PATCH',請求方法 //當沒有設置`transformRequest`時,必須是如下其中之一的類型(不可重複?): //-string,plain object,ArrayBuffer,ArrayBufferView,URLSearchParams //-僅瀏覽器:FormData,File,Blob //-僅Node:Stream data:{ firstName:'fred' }, //`timeout`定義請求的時間,單位是毫秒。 //若是請求的時間超過這個設定時間,請求將會中止。 timeout:1000, //`withCredentials`代表是否跨網站訪問協議, //應該使用證書 withCredentials:false //默認值 //`adapter`適配器,容許自定義處理請求,這會使測試更簡單。 //返回一個promise,而且提供驗證返回(查看[response docs](#response-api)) adapter:function(config){ /*...*/ }, //`auth`代表HTTP基礎的認證應該被使用,而且提供證書。 //這個會設置一個`authorization` 頭(header),而且覆蓋你在header設置的Authorization頭信息。 auth:{ username:'janedoe', password:'s00pers3cret' }, //`responsetype`代表服務器返回的數據類型,這些類型的設置應該是 //'arraybuffer','blob','document','json','text',stream' responsetype:'json', //`xsrfHeaderName` 是http頭(header)的名字,而且該頭攜帶xsrf的值 xrsfHeadername:'X-XSRF-TOKEN',//默認值 //`onUploadProgress`容許處理上傳過程的事件 onUploadProgress: function(progressEvent){ //本地過程事件發生時想作的事 }, //`onDownloadProgress`容許處理下載過程的事件 onDownloadProgress: function(progressEvent){ //下載過程當中想作的事 }, //`maxContentLength` 定義http返回內容的最大容量 maxContentLength: 2000, //`validateStatus` 定義promise的resolve和reject。 //http返回狀態碼,若是`validateStatus`返回true(或者設置成null/undefined),promise將會接受;其餘的promise將會拒絕。 validateStatus: function(status){ return status >= 200 && stauts < 300;//默認 }, //`httpAgent` 和 `httpsAgent`當產生一個http或者https請求時分別定義一個自定義的代理,在nodejs中。 //這個容許設置一些選選個,像是`keepAlive`--這個在默認中是沒有開啓的。 httpAgent: new http.Agent({keepAlive:treu}), httpsAgent: new https.Agent({keepAlive:true}), //`proxy`定義服務器的主機名字和端口號。 //`auth`代表HTTP基本認證應該跟`proxy`相鏈接,而且提供證書。 //這個將設置一個'Proxy-Authorization'頭(header),覆蓋原先自定義的。 proxy:{ host:127.0.0.1, port:9000, auth:{ username:'cdd', password:'123456' } }, //`cancelTaken` 定義一個取消,可以用來取消請求 //(查看 下面的Cancellation 的詳細部分) cancelToken: new CancelToken(function(cancel){ }) }
{ //`data`是服務器的提供的回覆(相對於請求) data{}, //`status`是服務器返回的http狀態碼 status:200, //`statusText`是服務器返回的http狀態信息 statusText: 'ok', //`headers`是服務器返回中攜帶的headers headers:{}, //`config`是對axios進行的設置,目的是爲了請求(request) config:{} }
axios.defaults.baseURL = 'https://api.example.com' axios.defaults.headers.common['Authorization'] = AUTH_TOKEN axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
你能夠在請求或者返回被 then 或者 catch 處理以前對他們進行攔截。
//添加一個請求攔截器 axios.interceptors.request.use( function(config) { //在請求發送以前作一些事 return config }, function(error) { //當出現請求錯誤是作一些事 return Promise.reject(error) } ) //添加一個返回攔截器 axios.interceptors.response.use( function(response) { //對返回的數據進行一些處理 return response }, function(error) { //對返回的錯誤進行一些處理 return Promise.reject(error) } )
項目中使用fetch 和axios 時,經常會作一些底層的封裝,好比一些統一的參數處理,異常處理,HTTP >= 300的處理
下一遍將嘗試對axios、fetch的封裝