終極非同步解決方案async,await以及非同步併發處理方案
前端js的傳統非同步解決方案及時回撥,but我們親愛的es6新增了三種解決方案:
- Promise
- Generator
- async
之前專案中一直是用promise來解決,vue專案中的axios其實也是返回的Promise物件,async其實算是Generator的語法糖,這次我們不講Promise 和Generator,因為對async一直理解不是很透徹,拿一個專案中的例項改造下async:
此專案用的vue+elementUI,這裡是一個CRM管理了系統,客戶檔案資訊管理模組,由於資訊比較龐大,分成三個標籤頁,三個單獨的介面資訊,自然分成三個子元件。
()
其中當父元件點選提交按鈕時,會分別請求三個介面
- 第一個介面資訊需要驗證,第二三個介面資訊不需要驗證
- 只有當第一個請求成功之後,才會請求後面兩個,
- 當後兩個請求成功之後返回上一頁。
index.vue
父元件點選提交觸發子元件submit事件
submitAll() {
this.$refs.baseInfo.submit()
/*if (this.buyer_info) {
this.$refs.businessInfo.submit()
this.$refs.chainInfo.submit()
this .$router.go(-1)
}*/
},
baseInfo.vue
*提交第一個子元件資訊*
submit() {
this.$refs['baseForm'].validate((valid) => {
if (valid) {
this.handleParams()
axios.post('/v2/buyer/createBuyerInfo',this.baseForm).then(res =>{
if(res.data.code == 1 ) {
this.$emit('isOK',true)
} else {
this.$message({
showClose: true,
message: `${res.data.message}`,
type: 'warning'
})
}
})
} else {
console.log('error submit!!');
return false;
}
});
},
index.vue
父元件監聽子元件的isOK事件
<base-info :i18n="i18n" ref='baseInfo' :isEdit='isEdit' @isOK='isOK' @tot='tot'></base-info>
子元件基本資訊請求通過,之後執行其他子元件事件
isOK(val) {
if(val) {
this.$message('成功')
this.$refs.businessInfo.submit()
this.$refs.chainInfo.submit()
this.$router.go(-1)
}
},
businessInfo.vue 和chainInfo.vue裡面的submit事件是兩個axios請求
submit() {
this.businessForm.purchase && this.handlerparams();
axios.post('/v2/buyerbusiness/createbusiness',{...this.businessForm,...{buyer_id: this.$route.query.id || this.buyer_info.id ,is_edit: this.$route.query.id ? true : false}}).then(res =>{
if(res.data.code == 1) {
} else {
this.$message({
showClose: true,
message: `業務資訊${res.data.message}`,
type: 'warning'
})
}
})
},
這裡面寫法比較麻煩,並且有個問題就是可能當子元件的請求沒有完成的時候就已經返回上一頁了
這裡可以用Promise.all的寫法解決,這裡我們嘗試下async/await,修改如下:
解決方案
index.vue
isOK(val) {
if(val) {
this.$message('成功')
const reqInOrder = async() =>{
try {
const a = await this.$refs.businessInfo.submit()
const b = await this.$refs.chainInfo.submit()
this.$router.go(-1)
} catch(e) {
console.log(e)
}
}
reqInOrder()
}
businessInfo.vue和chainInfo.vue
其中fetch是自己封裝的一個函式,返回的是個promise物件
當然axios本身返回的就是個promise物件,直接用就可以
submit() {
this.businessForm.purchase && this.handlerparams();
return fetch('/v2/buyerbusiness/createbusiness',{...this.businessForm,...{buyer_id: this.$route.query.id || this.buyer_info.id ,is_edit: this.$route.query.id ? true : false}})
},
fetch.js
export default function fetch(url, params) {
return new Promise((resolve, reject) => {
axios.post(url, params).then(response => {
resolve(response.data);
}, err => {
reject(err);
}).catch((error) => {
reject(error)
})
})
}
這裡還有個問題,由於businessInfo.vue和chainInfo.vue兩個介面是沒有先後順序的,而我們之前的寫法是繼發請求,也就是第一個介面請求完成後才請求第二個介面,這是不合理的。
優化方案
index.vue
isOK(val) {
if(val) {
this.$message('成功')
const reqInOrder = async() =>{
try {
併發處理請求(錯誤,其實是續發)
let businessSubmit = this.$refs.businessInfo.submit()
let chainSubmit = this.$refs.chainInfo.submit()
const a = await businessSubmit
const b = await chainSubmit
this.$router.go(-1)
} catch(e) {
console.log(e)
}
}
reqInOrder()
}
},
併發請求的兩種解決辦法
// 併發的兩種寫法
// 寫法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
// 寫法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
有啥問題還請多多指正。
——————————————我是分割線——————————————-
發現上面併發處理的是有問題的,businessInfo和chainInfo兩個請求是續發的,也就是businessInfo請求完之後才會請求chainInfo.
需求是第一個請求是續發,請求成功之後處理兩個併發請求,之後在處理相關業務
由於第一個請求是在子元件有相關的驗證規則以及處理,所以 單獨處理,成功之後emit isOK事件給父元件。
isOK(val) {
if(val) {
this.reqInOrder()
}
},
async reqInOrder() {
try {
// 處理併發
let children = [this.$refs.businessInfo,this.$refs.chainInfo]
//這裡陣列遍歷執行async ,map返回一個以Promise物件為key的陣列
const dataPromises = children.map( async child => {
const response = await child.submit()
return response
})
let codeArr = []
//處理Promise陣列得到resolve結果
for (const dataPromise of dataPromises) {
codeArr.push(await dataPromise)
}
//處理結果 之後處理相關資料
let flag = codeArr.every(item =>{
return item.code === 1
})
if (flag) {
this.fullscreenLoading = false
this.getCount()
}
} catch(e) {
console.log(e)
this.$message(e)
}
},