面试官:来, 实现一个基于promise的请求重试吧..
背景
实现一个函数 实现一个重试功能,当异步任务失败时,等待N秒后会自动重试直到成功或达到最大重试次数。
代码实现
/**
*
* @param task 返回一个promise的异步任务
* @param count 需要重试的次数
* @param time 每次重试间隔多久
* @returns 返回一个新promise
*/
const retry = (task, count = 5, time = 3 * 1000) => {
return new Promise((_res, _rej) => {
let doneCount = 0;
const run = () => {
task()
.then(data => {
_res(data);
})
.catch(err => {
doneCount++;
if (doneCount > count) {
_rej(err);
} else {
setTimeout(run, time);
}
});
};
run();
});
};
retry(query);
代码分析
query
函数返回一个fetch
请求的结果,这个结果是一个Promise
。retry
函数创建并返回一个新的Promise
,内部使用递归和setTimeout
来实现重试逻辑。- 每次
task
执行失败时,catch
块会捕获错误,并增加doneCount
计数器。 - 如果
doneCount
超过了count
,retry
函数会拒绝Promise
并传递错误。 - 如果没有超过重试次数,
setTimeout
会延迟执行run
���数,从而实现重试。
知识点
fetch
API:
- 用于发起网络请求,获取资源。
fetch
返回一个Promise
对象,可以链式调用.then()
和.catch()
处理响应和错误; fetch()
采用模块化设计,API 分散在多个对象上(Response 对象、Request 对象、Headers 对象);- 通过数据流(Stream 对象)处理数据,可以分块读取,有利于提高网站性能表现,减少内存占用,对于请求大文件或者网速慢的场景相当有用。
更多关于fetch使用 可以参考这篇文章。
Promise
:
异步控制流 Asynchronous Control Flow:
"异步控制流"是指在编程中,特别是在涉及异步操作(如I/O操作、网络请求等)的编程中,管理和控制这些操作执行顺序和时间点的方法和策略。例如上面是通过 Promise
和 setTimeout
来控制异步操作的执行顺序。
在JavaScript中,由于其单线程的特性,异步控制流尤为重要,因为它允许程序在等待异步操作完成时继续执行其他代码,从而提高效率和响应性。 异步控制流还包括错误处理机制,确保当异步操作失败时,程序能够适当地响应并继续执行。 在异步控制流中,可以控制多个异步操作的并发执行,以及它们之间的同步关系,例如,只有在前一个异步操作完成后,才开始下一个操作。 合理的异步控制流可以减少等待时间,提高程序性能,特别是在I/O密集型或网络请求密集型的应用中。
递归:
run
函数通过递归调用自身来实现重试逻辑。每次请求失败后,会等待指定的时间间隔,然后再次尝试执行 task
。
在JavaScript中,递归是指一个函数在其内部直接或间接地调用自身。递归通常用于解决一些具有自相似性的问题,即问题可以通过缩小或分解为相似的子问题来求解。在递归中,通常有以下两个关键部分:
- 基本情况(Base case):用于标识问题规模最小时的解决方案。基本情况是直接可解的,不需要递归求解。递归在到达基本情况时停止。
- 递归规模递减(Recursive case):把问题分解成更小规模的子问题,并调用自身来解决这些子问题。每次递归调用时,问题规模都应该减小,最终收敛到基本情况。
例如:
function fibonacci(n) {
// 基本情况 (n为0或1时,直接返回n)
if (n === 0 || n === 1) {
return n;
}
// 递归规模递减(求解子问题)
else {
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
递归在使用时需要格外注意,因为不合适或错误地使用递归可能导致栈溢出(调用栈的大小超过限制)或死循环。为了避免这些问题,请确保问题每次递归调用时都缩小了规模,并且在到达基本情况时停止递归。
闭包:
run
函数是一个闭包,它可以访问其外部作用域的变量,如 doneCount
。
闭包的本质是函数,捕获了其静态词法作用域,从而形成了作用域链。这使得函数能够跨越其定义环境访问外部变量,是对作用域链和内存引用管理的深刻理解。
闭包揭示了 JavaScript 函数作为一等公民的重要特征。函数不仅可以作为值传递,还能保留其定义时的上下文,这为函数式编程提供了强大支持。
通过闭包,我们能够创建私有变量、实现数据封装和模块化,提升代码的安全性和可维护性。因此,掌握闭包是全面理解和高效使用 JavaScript 的关键。
使用示例:
请求成功的case:
请求失败的case:
第N次成功
let exeCount = 0;
const query2 = () => {
return new Promise((_res, _rej) => {
setTimeout(() => {
++exeCount > 3 ? _res(exeCount) : _rej();
}, 1000);
});
};
retry(query2)
.then(res => {
console.log('succ:', res);
})
.catch(err => {
console.log('err:', err);
});
重试了3次后第四次 请求成功 返回最终结果
系列文章
相关文章
转载自:https://juejin.cn/post/7395623789135265827