高階函式的使用

hcfw007發表於2019-02-17

問題

位元組跳動面試時問題:原函式例如fetchData是一個非同步函式,嘗試從伺服器端獲取一些資訊並返回一個Promise。寫一個新的函式可以自動重試一定次數,並且在使用上和原函式沒有區別。

思路

這個問題其實不是很難,不過可能是太菜了緊張的原因,當時答得不是很好。不過思路還是很明確的,內部通過閉包來計數,一旦成功獲得資料就返回,否則就繼續嘗試,知道重試次數達到上限位置。

function retry(fetch, n) {
    let i = 0
    function tryFetch(err) {
        if (i > n) {
            return "reach try limit:" + err
        } else {
            fetch().then(data => {
                return data
            }).catch(err => {
                i ++
                tryFetch(err)
            })
        }
    }
}

當時差不多就是那麼答的,有幾個問題是,函式呼叫方式與原來不通,按道理應該返回一個Promise,更準確的說是返回一個返回Promise的函式,如果有什麼問題也應該在外面能catch住。另一方面,也許fetch函式要接受引數,也應該傳遞進去才行。

解決

修改後的函式如下

function retry(fetch, n) {
    return function() {
        let args = arguments
        return new Promise((rseolve, reject) => {
            let i = 0
            tryFetch()
            function tryFetch(err) {
                console.log(i)
                if (i > n) {
                    reject("reach max try" + err)
                } else {
                    fetch(...args).then(data => {
                        rseolve(data)
                    }).catch(err => {
                        i ++
                        tryFetch(err)
                    })
                }
            }
        })
    }
}

最後自己寫了個fetch進行測試

function fetch() {
    console.log(arguments)
    return new Promise((rseolve, reject) => {
        console.log(`trying...`)
        setTimeout(function() {
            if (Math.random() > 0.9) {
                console.log(`resolved`)
                rseolve(`resolved`)
            } else {
                console.log(`rejected`)
                reject(`rejected`)
            }
        }, 5000)
    })
}

結果符合預期,問題解決。當然也可以返回async function,不過和Promise本質上是一個思路。

相關文章