【面試Vue全家桶】vue前端交互模式-es7的語法結構?async/await
- 2020 年 2 月 13 日
- 筆記

作者 | Jeskson
掘金 | https://juejin.im/user/5a16e1f3f265da43128096cb
2020.1.12
前端發請求,調用後端接口,來獲取特定格式的數據,老闆問了,你會前後端交互模式嗎?vue的那種。異步接口調用,常常使用到的語法,promise的概念是什麼呢?調用接口的方式,第一種為,fetch進行接口調用,第二種為,axios進行接口的調用。
es7的語法結構?async/await方式調用接口,基於不同接口的調用方式來實現案例。
讓我們了解一下前後端的交互模式,學習一下promise的語法,來回答面試官的問題,基於promise來實現異步調用,就算你會promise?那你了解fetch用法和async/await用法嗎?處理異步調用接口的方式。
網上一圖,回調地獄:看到暈,使代碼難以理解和維護。

前後端的交互是什麼
前後端的交互就是前端的瀏覽器去調用後端的接口,拿到後端的數據,在做前端的處理,進行渲染。客戶端與服務器的通信模式,前後端交互,調用接口的方法,第一種,原生的ajax,第二種,基於jquery的ajax,第三種,基於fetch,第四種,基於axios。
前端是通過請求地址向後端發送請求的,標準的url格式是什麼樣的呢?
格式如下:
schema://host:port/path?query#fragment
第一個schema為協議,比如http,https,ftp等,第二個,host為域名或者ip地址,第三個port為端口,http默認為80端口,是可以省略的,第四個path為路徑,比如/index,第五個query為查詢參數,比如name=dada&age=12,第六個fragment為錨點,哈希Hash,是用於定位頁面的某個位置。
符合規則的url有哪些是正確的呢?
http://www.dada.cnhttp://www.dada.cn/index/dadahttp://www.dada.cn/index/dada?name=dadahttp://www.dada.cn/index/dada?name=dada#theme
新型的url地址,restful形式的。HTTP的請求方式,第一種,使用GET為查詢,第二種,使用POST為添加,第三種,使用PUT為修改,第四種,使用DELETE為刪除。
符合規則的url地址:
http://www.dada.com/index GEThttp://www.dada.com/index POSThttp://www.dada.com/index/1 PUThttp://www.dada.com/index/2 DELETE
promise對象
promise用於表示一個異步操作的最終完成(或失敗),以及結果值。
Promise對象有以下兩個特點
對象的狀態不受外界影響
一旦狀態改變,就不會再變,任何時候都可以得到這個結果
constpromise1 =newPromise(function(resolve, reject){ setTimeout(function(){ resolve('foo'); },300);});promise1.then(function(value){console.log(value);// expected output: "foo"});console.log(promise1);// expected output: [object Promise]
語法:
newPromise(function(resolve, reject){…}/* executor */);

因為 Promise.prototype.then 和 Promise.prototype.catch 方法返回promise 對象, 所以它們可以被鏈式調用。



promise用法
promise是什麼呢?它是用於異步計算,將異步操作隊列化,按照期望的順序執行,返回符合預期的結果,可以在對象之間傳遞和操作promise。

創建promise
constda =newPromise((resolve, reject) =>{// resolve(someValue); // fulfilled// reject("failure reason"); // rejected});
functionda(url){returnnewPromise((resolve, reject) =>{constxhr =newXMLHttpRequest(); xhr.open("GET", url); xhr.onload =()=>resolve(xhr.responseText); xhr.onerror =()=>reject(xhr.statusText); xhr.send(); });};
簡單的實例
newPromise(resolve=>{ setTimeout(()=>{ resolve('hello') },2000)}).then(res=>{console.log(res)})
異步操作,事件監聽
document.getElementById('#start').addEventListener('click',start,false);functionstart(){// 響應事件,進行相應的操作}//jquery on 監聽$("#start").on("click", start)
回調
$.ajax(url, { success(res) { }})// 在頁面加載完畢後回調$(function(){// 頁面結構加載完成})
在JavaScript中,異步情況,第一種為定時任務,第二種為ajax,第三種事件函數。
newPromise(function(resolve, reject){ resolve('成功')// 數據處理完成// reject('失敗') // 數據處理出錯}).then((res) =>{console.log(res)},// 成功(err) => {console.log(err)}// 失敗)
異步編程與promise
$.ajax({
url: '',
success: function(data) {
console.log(data)
}
});
index.js
constexpress =require('express')constapp = express()constbodyParser =require('body-parser')// 處理靜態資源app.use(express.static('public'))// 處理參數app.use(bodyParser.json());app.use(bodyParser.urlencoded({extended:false}));// 設置允許跨域訪問服務app.all("*",function(req,res,next){ res.header("Access-Control-Allow-Origin","*"); res.header("Access-Control-Allow-Methods","PUT,GET,POST,DELETE,OPTIONS"); res.header("Access-Control-Allow-Headers","X-Requested-With"); res.header("Access-Control-Allow-Headers","Content-Type"); next();});// 路由app.get('/data', (req,res) => { res.send("hello world!")})// 啟動監聽app.listen(3000, () => {console.log("runing…")})

異步編程,多次異步調用,結果順序結果不確定

promise是異步編程的一種解決方案,從語法上來講,promise是一個對象,從它可以獲取異步操作的消息。使用promise的好處有哪些呢?
它可以避免多層異步調用嵌套問題(回調地獄),promis對象提供了簡潔的api,使得控制異步操作更加容易。回調地獄,多層嵌套請求問題,請求接口調用後台數據,有兩種可能性,一種為成功回調,一種為失敗回調,成功後寫一下成功後的操作代碼,失敗後也要寫一下失敗後的操作代碼。
在JavaScript中的世界裏,所有的代碼都是單線程執行的。因為這個缺點,所以會導致在JavaScript中的所有網絡操作,瀏覽器事件,都必須是異步執行的,異步執行可以用回到函數實現。
functioncallback(){console.log("dada");}console.log("dada setTimeout()");setTimeout(callback,1000);// 1秒後調用callback函數
注意,異步操作會在將來某個時間點觸發一個函數調用。
ajax的經典異步操作
request.onreadystatechange = function() {
if(request.readyState === 4) {
if(request.status === 200) {
return success(request.responseText);
}else{
return fail(request.status);
}
}
}
'usestrict';
new Promise(function() {} );
// 直接運行測試:
console.log('支持Promise!');
如果同時發送多個ajax的請求,返回來的結果是不確定的,要想返回的結果順序確定下來,就必須進行嵌套,如果嵌套就會有回調地獄的問題,這樣導致的代碼可讀性就會降低,所以就有promise語法來解決這一回調地獄的問題。
所以promise的出現的好處就是為了解決地獄回調,可以避免嵌套問題,和簡潔的代碼結構,可讀性增強。
console.log(typeofPromise)
示例
letda =newPromise(function(resolve, reject){// 當異步代碼執行成功時,會調用 resolve(…)// 當異步代碼失敗時, 會調用 reject(…)//使用setTimeout(…)來模擬異步代碼setTimeout(function(){ resolve("成功!"); },250);});da.then(function(res){//res的值是上面調用resolve(…)方法傳入的值.console.log("dada"+ res);});
promise的基本用法
首先實例化promise對象,構造函數中傳遞函數,該函數中用於處理異步任務,有兩個參數,resolve和reject用於處理成功和失敗的兩種情況,並通過p.then獲取處理結果。
then()方法返回一個promise:
constda =newPromise(function(resolve, reject){ resolve('da!');});da.then(function(value){console.log(value);// expected output: "da!"});
語法
p.then(onFulfilled[, onRejected]);
p.then(value => {
// fulfillment
}, reason => {
// rejection
});
var dada = new Promise((resolve, reject) => {
resolve('成功!');
// or
// reject(new Error("出錯了!"));
});
dada.then(value => {
console.log(value); // 成功!
}, reason => {
console.error(reason); // 出錯了!
});
catch()方法返回一個promise,並且處理拒絕的情況
p.catch(onRejected);p.catch(function(reason){// 拒絕});
finally()方法返回一個promise,在promise結束時,無論結果是fulfilled或者是rejected,都會執行指定的回調函數。
p.finally(onFinally);p.finally(function(){// 返回狀態為(resolved 或 rejected)});
newPromise(resolve=>{ setTimeout(()=>{ resolve('hello') },2000) }).then(val=>{console.log(val)// 參數val = 'hello'returnnewPromise(resolve=>{ setTimeout(()=>{ resolve('world') },2000) }) }).then(val=>{console.log(val)// 參數val = 'world'})
代碼例子:
varp =newPromise(function(resolve, reject){// 成功回調 resolve()// 失敗回調 reject()});p.then(function(ret){// resolve得到正常的結果},function(ret){// reject得到錯誤的結果});
resolve作用為將promise對象的狀態從「未完成」變成為「成功」,即是從Pending變為resolved,在異步操作成功時調用,並將異步操作的結果,作為參數傳遞出去,而reject的作用是將promise對象的狀態從「未完成」變成「失敗」,就是從Pending變成rejected,在異步操作失敗時調用,並將異步操作報出的錯誤,作為參數傳遞出去。
promise有三種狀態,第一種為Pending,待定,初始狀態,第二種狀態為fulfilled,實現,操作成功,第三種狀態為rejected,被否決,操作失敗。
當promise狀態發生改變時,就會觸發then()裏面的響應函數處理,promise狀態一旦改變,就不會再變了。所以promis對象的狀態改變有兩種情況,第一種,從pending變為fulfilled,第二種為,從pending變為rejected。

基於promise處理ajax請求,處理原生ajax
functionqueryData(url){returnnewPromise(function(resolve,reject){varxhr =newXMLHttpRequest(); xhr.onreadystatechange =function(){if(xhr.readyState !=4)return;if(xhr.status ==200) { resolve(xhr.responseText) }else{ reject('出錯了'); } } xhr.open('get',url); xhr.send(null); })}
發送多個ajax請求
queryData()
.then(function(data){
return queryData();
})
.then(function(data){
return queryData();
})
.then(function(data){
return queryData();
});
// return 是新的promise對象
then參數中的函數返回值
第一種,返回promsie實例對象,返回的實例對象會調用下一個then
第二種,返回普通值,返回的普通值會直接傳遞給下一個then,通過then參數中函數的參數接收該值
promise常用的api
實例方法有三種,第一種,p.then()得到異步任務的正確結果,第二種,p.catch()獲取異常信息,第三種,p.finally()成功與否都會執行。
queryData() .then(function(data){console.log(data); }) .catch(function(data){console.log(data); }) .finally(function(){console.log('finished');});
promise常用api-實例方法
functionda(){returnnewPromise(function(resolve, reject){ setTimeout(function(){// resolve(123);reject('error'); },100);})}da() .then(function(data){console.log(data) }) .catch(function(data){console.log(data) }) .finally(function(){console.log('dada') });
da().then(function(data){console.log(data)},function(data){console.log(data)}).finally(function(){console.log('dada')});
對象方法
promise.all()並發處理多個異步任務,所有任務都執行完成才能得到結果
promise.race()並發處理多個異步任務,只要有一個任務完成就能得到結果
Promise.all([p1,p2,p3]).then(result) => {console.log(result);})Promise.race([p1,p2,p3]).then(result) => {console.log(result);})
代碼:
functionqueryData(url){returnnewPromise(function(resolve, reject){varxhr =newXMLHttpRequest(); xhr.onreadystatechange =function(){if(xhr.readyState !=4)return;if(xhr.readyState ==4&& xhr.status ==200) {// 處理正常的情況resolve(xhr.responseText); }else{// 處理異常情況reject('服務器出錯'); }; xhr.open('get',url); xhr.send(null); }); }varp1 = queryData(url);varp2 = queryDate(url1);Promise.all([p1,p2]).then(function(result){console.log(result)})
在promise中常用到回調函數延時綁定,返回值,錯誤冒泡。

constpromise =newPromise((resolve, reject) =>{console.log(1) resolve()console.log(2)})promise.then(()=>{console.log(3)})console.log(4)VM49:21VM49:42VM49:94VM49:73undefined
其中,promise構造函數是執行同步的作用,promise.then是執行異步函數的操作。
letpro =newPromise(resolve=>{ setTimeout(()=>{ resolve('hello world') },2000) }) setTimeout(()=>{ pro.then(value=>{console.log(value)// hello world}) },2000)
接口調用fetch的用法
fetch的概述,它更加簡單的數據獲取方式,功能更加強大,更加靈活,基於promise實現的。
語法結構:
fetch(url).then().then()….catch()
fetch的基本用法
fetch('/da').then(data=>{returndata.text();}).then(ret={console.log(ret);});
text()方法屬於fetchapi中的一部分,它返回一個promise實例對象,用於獲取後台返回的數據。
fetch請求參數
method(string)
http請求方法,默認為GET,可以使用POST,PUT,DELETE
body(string)
http的請求參數
headers(object)
http的請求頭
fetch('/da', { method;'get'}).then(data=>{returndata.text();}).then(ret=>{console.log(ret);});
GET請求方式的參數傳遞
fetch('/da?id=123').then(data=>{returndata.text();}).then(ret=>{console.log(ret);});fetch('/da/123', {method:'get'}).then(data=>{returndata.text();}).then(ret=>{console.log(ret);});
delete請求方式的參數傳遞
fetch('/da/123', {method:'delete'}).then(data=>{returndata.text();}).then(ret=>{console.log(ret);});
fetch請求參數的post請求方式的參數傳遞
fetch('/da', {method:'post',body:'name=dada',headers: {'content-Type':'application/x-www-form-urlencoded', } }).then(data=>{returndata.text(); }).then(ret=>{console.log(ret); });
post請求方式的參數傳遞
fetch('/da', {method:'post',body:JSON.stringify({name:'dada',age:12}) headers;{'Conent-Type':'application/json', } }).then(data=>{returndata.text(); }).then(ret=>{console.log(ret);});
fetch響應結果
響應數據格式
text()
將返回體處理成字符串類型
json()
返回結果和json.parse(presponseText)相同
接口調用axios用法
第三方的庫,很強大,是一個局域promise用於瀏覽器和node.js的HTTP客戶端。
它的特性,第一點是支持瀏覽器和node.js,第二點是支持promise,第三點,能夠攔截請求和響應,第四點,可以自動轉換json類型。
axios的基本用法
axios.get('/dada').then(ret=>{console.log(ret.data);});
axios的常用api
get,查詢數據,post,添加數據,put,修改數據,delete,刪除數據。
get傳遞參數,第一,通過url傳遞參數,第二種,通過params傳遞參數
axios.get('/da?id=123').then(ret=>{console.log(ret.data);})
restful傳參
axios.get('/dada/123')
.then(ret=>{
console.log(ret.data)
})
axios.get('/da', {params: { id:123}}).then(ret=>{console.log(ret.data)})
delete傳遞參數
參數傳遞方式與get類似
axios.delete('/da?id=1)
.then(ret=>{
console.log(ret.data)
})
axios.delete('/da/2)
.then(ret=>{
console.log(ret.data)
})
axios.delete('/da', {
params: {
id:1
}
})
.then(ret=>{
console.log(ret.data);
})
post傳遞參數
axios.post('/da', { name;'dada',}).then(res=>{console.log(res.data)})
默認傳遞的是json格式的數據。
post傳遞參數,通過URLSearchParams傳遞參數
application/x-www-form-urlencoded
constparams =newURLSearchParams();params.append('param1','value1');params.append('param2','value2');axios.post('/api/da', params).then(res=>{console.log(res.data)})
put傳遞參數
參數傳遞方式與post類似
axios.put('/da/1', {name:'dada',}).then(res=>{console.log(res.data)})
axios的響應結果
data為響應回來的數據,headers為響應頭信息,status為響應狀態碼,statusText響應狀態信息。
axios.post('/da-json').then(res=>{
console.log(res)
})
axios的全局 配置
axios.default.timeout = 3000; // 超時時間
axios.default.baseURL = 'http…' // 默認地址
axios.default.headers['mytoken'] = 'xxxx' //設置請求頭
// 配置請求的基準url地址axios.default.baseURL ='http://localhost:xxx/';axios.get('da-json').then(function(res){console.log(res.data.name);});
axios攔截器
axios.interceptors.request.use(function(config){//在拿過去發出之前進行一些信息設置returnconfig;},function(err){// 處理響應的錯誤信息});
在獲取數據之前對數據做一些加工處理。
接口調用async/await用法
async/await是es7引入的語法,更加方便了異步操作。

asyncfunctionqueryData(id){constres =awaitaxios.get('/data');returnres;}queryData.then(res=>{console.log(res)})
async關鍵字用於函數上,await關鍵字用於async函數中。
asyncfunctionname([param[, param[, … param]]]){ statements }name: 函數名稱param: 要傳遞給函數的參數的名稱statements: 函數體語句返回值: 返回的Promise對象會以asyncfunction的返回值進行解析或者以該函數拋出的異常進行回絕。
多個異步請求的async/await處理
asyncfunctionqueryData(id){constda1 =awaitaxios.get('/da1');constda2 =awaitaxios.get('/da2?name=dada');returnres;}queryData.then(res=>{console.log(res)})
出現了async/await之前,我們有三種異步書寫我們的代碼,第一,嵌套回調,第二,以promise為主的鏈式回調,使用generators。

async function dada(x) {
let a = 1;
return x+a;
}
undefined
dada(10);
Promise {<resolved>: 11}__proto__:
Promisecatch: ƒ catch()constructor:
ƒ Promise()finally: ƒ finally()then:
ƒ then()Symbol(Symbol.toStringTag):
"Promise"__proto__: Object[[PromiseStatus]]:
"resolved"[[PromiseValue]]:
11
await只能在async函數內部使用,用在普通函數里就會報錯。async/await實際上是Generator的語法糖。async關鍵字代表後面的函數中有異步操作,await表示等待一個異步方法執行完成。async 函數返回一個Promise對象,因此 async 函數通過 return 返回的值,會成為 then 方法中回調函數的參數。
await 就是異步等待,它等待的是一個Promise,async函數調用不會造成代碼的阻塞,但是await會引起async函數內部代碼的阻塞。
帶async關鍵字的函數,是聲明異步函數,返回值是promise對象
asyncfunctiontest(){return'da'}test();返回值為Promise{:"da"}。

宏任務和微任務都是隊列
宏任務有script、setTimeout、setInterval等
微任務有Promise.then,catch,finally,process.nextTick等

參考地址
https://www.liaoxuefeng.com/wiki/1022910821149312/1023024413276544