【javascript】實現一個封裝請求ajax器

前言

  • 這個是羣裏朋友問的,感覺很有意思。
  • 要求是這樣:
  • 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秒時間。

初步實現包裝

  • 將其包裝,讓用戶調用時直接then能收到數據。
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))
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章