問題
字節跳動面試時問題:原函數例如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本質上是一個思路。