前言
- 這個是羣裏朋友問的,感覺很有意思。
- 要求是這樣:
- 1、限制一次同時發送的ajax請求數量
- 2、timeout限制
- 3、重試n次。
- 給了段開頭代碼:
const axios = require('axios')
axios.post()
- 另外說了這個限制數量的話,超出的要等待前面完成才行。
分析
- 首先這個超時axios裏是有配置的,就算寫XMLHttpRequest或者ie那個,也得在上面設置。
- 然後是限制發送請求數量,這個比較難,要麼在源碼里弄,要麼就自己封裝,自己封裝還有點麻煩,因爲你要保證每個都能獲取到最後結果。
- 重試次數也好搞,就是reject做遞歸。
- 但是這些異步結合起來就很亂,得設計好怎麼調用再寫。
- 一般來說,先把大問題全拆開來,再考慮後面合併。
- 所以先做個限定進出並可以等待的隊列。然後再思考如何利用axios的回調。
限定進出可等待隊列
- 我一開始準備用redux思路寫,但redux那個執行就直接foreach然後fn都執行了,這樣就沒法控制已經完成了幾個異步函數並讓等待隊列中的去執行。
- 所以我想到了webpack中有個庫叫semaphore,這個思路類似於redux,但是放入的異步函數執行完畢要調用它的leave,通過這種調用方式,可以得知任務的完成,從而進行判斷是否有等待的函數需要被執行。
class ArrList {
constructor(available) {
this.available = available
this.waiters = []
this._continue = this._continue.bind(this)
}
take(task) {
if (this.available > 0) {
this.available--;
task()
} else {
this.waiters.push(task)
}
}
leave() {
this.available++
if (this.waiters.length > 0) {
this._continue()
}
}
_continue() {
if (this.available > 0) {
this.available--;
let task = this.waiters.shift()
if (task) {
task()
}
}
}
}
let ins = new ArrList(2)
console.time('cost')
ins.take(
() => setTimeout(() => {
console.log(1)
ins.leave()
}, 1000)
)
ins.take(
() => setTimeout(() => {
console.log(2)
ins.leave()
}, 1000)
)
ins.take(
() => setTimeout(() => {
console.log(3)
ins.leave()
}, 1000)
)
ins.take(
() => setTimeout(() => {
console.log(4)
ins.leave()
}, 1000)
)
ins.take(
() => setTimeout(() => {
console.log(5)
ins.leave()
}, 1000)
)
ins.take(
() => setTimeout(() => {
console.log(6)
ins.leave()
}, 1000)
)
ins.take(
() => setTimeout(() => {
console.log(7)
ins.leave()
console.timeEnd('cost')
}, 1000)
)
- 我這裏take了7個異步函數,隊列最多2個執行,所以需要花費ceil(7/2)=4秒時間。
初步實現包裝
let axios = require('axios')
class MyAxios {
constructor(listLength) {
this.arrIns = new ArrList(listLength)
}
axios(method, url, config) {
return new Promise((resolve, reject) => {
this.arrIns.take(() => axios[method](url, config).then(res => {
resolve(res)
this.arrIns.leave()
}).catch((err) => {
reject(err)
this.arrIns.leave()
}))
})
}
}
let myaxiosIns = new MyAxios(2)
myaxiosIns.axios('get', 'http://localhost:4002/dd', { timeout: 3000 }).then(res => {
console.log(res.data)
}).catch(res => console.log(res.data))
myaxiosIns.axios('get', 'http://localhost:4002/dd', { timeout: 3000 }).then(res => {
console.log(res.data)
}).catch(res => console.log(res.data))
myaxiosIns.axios('get', 'http://localhost:4002/dd', { timeout: 3000 }).then(res => {
console.log(res.data)
}).catch(res => console.log(res.data))
myaxiosIns.axios('get', 'http://localhost:4002/dd', { timeout: 3000 }).then(res => {
console.log(res.data)
}).catch(res => console.log(res.data))
遞歸重試
- 這個配合嵌套這麼多的異步就容易暈了。用遞歸老方法,使用全局變量,然後提取函數就可以了:
class MyAxios {
constructor(listLength) {
this.arrIns = new ArrList(listLength)
}
axios(method, url, config, retryNum) {
return new Promise((resolve, reject) => {
let retry = 0
let fn = () => axios[method](url, config).then(res => {
resolve(res)
this.arrIns.leave()
}).catch((err) => {
if (retry < retryNum) {
retry++
fn()
} else {
reject(err)
this.arrIns.leave()
}
})
this.arrIns.take(fn)
})
}
}
let myaxiosIns = new MyAxios(2)
myaxiosIns.axios('get', 'http://localhost:4002/dd', { timeout: 3000 }, 3).then(res => {
console.log(res.data)
}).catch(res => console.log(res.data))
myaxiosIns.axios('get', 'http://localhost:4000/', { timeout: 3000 }, 2).then(res => {
console.log(res.data)
}).catch(res => console.log(res.data))
myaxiosIns.axios('get', 'http://localhost:4002/dd', { timeout: 3000 }, 1).then(res => {
console.log(res.data)
}).catch(res => console.log(res.data))
myaxiosIns.axios('get', 'http://localhost:4002/dd', { timeout: 3000 }, 0).then(res => {
console.log(res.data)
}).catch(res => console.log(res.data))