likes
comments
collection
share

面试官:来, 实现一个基于promise的请求重试吧..

作者站长头像
站长
· 阅读数 29

背景

实现一个函数 实现一个重试功能,当异步任务失败时,等待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 超过了 countretry 函数会拒绝 Promise 并传递错误。
  • 如果没有超过重试次数,setTimeout 会延迟执行 run 函数,从而实现重试。

知识点

fetch API

  1. 用于发起网络请求,获取资源。fetch 返回一个 Promise 对象,可以链式调用 .then().catch() 处理响应和错误;
  2. fetch()采用模块化设计,API 分散在多个对象上(Response 对象、Request 对象、Headers 对象);
  3. 通过数据流(Stream 对象)处理数据,可以分块读取,有利于提高网站性能表现,减少内存占用,对于请求大文件或者网速慢的场景相当有用。

更多关于fetch使用 可以参考这篇文章。

Promise

异步控制流 Asynchronous Control Flow

"异步控制流"是指在编程中,特别是在涉及异步操作(如I/O操作、网络请求等)的编程中,管理和控制这些操作执行顺序和时间点的方法和策略。例如上面是通过 PromisesetTimeout 来控制异步操作的执行顺序。

在JavaScript中,由于其单线程的特性,异步控制流尤为重要,因为它允许程序在等待异步操作完成时继续执行其他代码,从而提高效率和响应性。 异步控制流还包括错误处理机制,确保当异步操作失败时,程序能够适当地响应并继续执行。 在异步控制流中,可以控制多个异步操作的并发执行,以及它们之间的同步关系,例如,只有在前一个异步操作完成后,才开始下一个操作。 合理的异步控制流可以减少等待时间,提高程序性能,特别是在I/O密集型或网络请求密集型的应用中。

递归

run 函数通过递归调用自身来实现重试逻辑。每次请求失败后,会等待指定的时间间隔,然后再次尝试执行 task

在JavaScript中,递归是指一个函数在其内部直接或间接地调用自身。递归通常用于解决一些具有自相似性的问题,即问题可以通过缩小或分解为相似的子问题来求解。在递归中,通常有以下两个关键部分:

  1. 基本情况(Base case):用于标识问题规模最小时的解决方案。基本情况是直接可解的,不需要递归求解。递归在到达基本情况时停止。
  2. 递归规模递减(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面试官:来,  实现一个基于promise的请求重试吧..

闭包的本质是函数,捕获了其静态词法作用域,从而形成了作用域链。这使得函数能够跨越其定义环境访问外部变量,是对作用域链和内存引用管理的深刻理解。

闭包揭示了 JavaScript 函数作为一等公民的重要特征。函数不仅可以作为值传递,还能保留其定义时的上下文,这为函数式编程提供了强大支持。

通过闭包,我们能够创建私有变量、实现数据封装和模块化,提升代码的安全性和可维护性。因此,掌握闭包是全面理解和高效使用 JavaScript 的关键。

使用示例:

请求成功的case:

面试官:来,  实现一个基于promise的请求重试吧..

请求失败的case:

面试官:来,  实现一个基于promise的请求重试吧..

第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次后第四次 请求成功 返回最终结果 面试官:来,  实现一个基于promise的请求重试吧..

系列文章

相关文章

转载自:https://juejin.cn/post/7395623789135265827
评论
请登录