【node实战系列】编写一个重试装饰器

file

本文首发于:https://github.com/bigo-frontend/blog/ 欢迎关注、转载。

背景

bigo前端开始推广bff,hello农场作为首个bff落地项目,历经2个月,完成了从0-1的落地实践。

【node实战系列】按照小模块拆分,从开发者的角度讲叙,如何进行bff高可用编码。

本系列文章,基于eggjs框架编码,使用ts语法,为了提升阅读体验,建议大家先了解一下eggjs。

系列文章

  • 【node实战系列】编写一个重试装饰器
  • 【node实战系列】自行实现应用缓存
  • 【node实战系列】异步并发,自定义Promise.allSettled
  • 【node实战系列】rpc与http协议通讯
  • 【node实战系列】使用reqId跟踪请求全流程日志
  • 【node实战系列】入参校验validate
  • 【node实战系列】异常中断
  • 【node实战系列】base64编码
  • 【node实战系列】服务发现
  • 【node实战系列】编码与约定
  • 【node实战系列】监控告警
  • 【node实战系列】单元测试
  • 【node实战系列】压测
  • 【node实战系列】灰度
  • 【node实战系列】文档
  • 【node实战系列】系列小结

欢迎大家关注我们的github blog,持续更新。
https://github.com/bigo-frontend/blog/issues

为什么要重试

这个很简单,项目开发中,调用第三方接口会因为网络延迟、异常导致调用的服务出错,重试几次可能就会调用成功(例如上传图片),所以需要一种重试机制进行接口重试来保证接口的正常执行。

为什么要用装饰器

其实我们可以在接口调用外面再包装一个重试方法,如下编码。

/**
 *
 * @param {number} retries - 重试次数
 * @param {Function} fn - 重试函数
 */
const retry = (retries, fn) => {
  fn().catch((err) => retries > 1 ? retry(retries - 1, fn) :  Promise.reject(err));
};

但是这种函数嵌套阅读起来不优雅,并且我们使用了ts进行编码,应该要物尽其用,发挥其装饰器能力。

装饰器简单来说就是对类或者其属性进行拓展,便于添加额外的功能。

什么是装饰器

先来看一下retry装饰器在代码中是长成什么样子吧

@retry(3, (res) => res.code !== 0)
public async initFarm() {
  const res = await this.request({
    opType: Eoperator.BASE_INIT_FARM,
  });
  return res;
}

代码中@retry(3, (res) => res.code !== 0)就是一个装饰器了

以 @ 作为标识符,可以作用于类,也可以作用于类的属性,具体使用原理请查看文档 https://www.tslang.cn/docs/handbook/decorators.html

方法装饰器

我们的重试装饰器就是一个方法装饰器,我们先讲解一下方法装饰器的各属性

下面是一个方法装饰器(@enumerable)的例子,应用于Greeter类的方法上:

class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }

    @enumerable(false)
    greet() {
        return "Hello, " + this.greeting;
    }
}

我们可以用下面的函数声明来定义@enumerable装饰器:

function enumerable(value: boolean) {
    console.log(value); // 入参,false
    // target 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
    // propertyKey 成员的名字
    // descriptor 成员的属性描述符
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
      descriptor.enumerable = value;
    };
}

实现重试装饰器

// 休眠,延迟执行
function sleep(duration) {
  return new Promise((reslove) => setTimeout(reslove, duration))
}
/**
 * 重试装饰器
 *
 * @param {number} retries 重试次数
 * @param {*} cb 重试条件
 * @returns
 */
export function retry(retries: number, cb) {
  return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    // 1. 保存原方法体
    const oldMethod = descriptor.value;
    // 2. 重新定义方法体
    descriptor.value = async function(...args) {
      // 重试
      const loop = async (count) => {
        // 3. 执行原来的方法体
        const res = await oldMethod.apply(this, args);
        if (count > 1 && cb(res)) {
          sleep(100); // 休眠100ms,再重试
          return loop(--count);
        } else {
          return res;
        }
      };
      return loop(retries);
    }
  }
}

小结

回顾全文,我们发现一个小而美的重试装饰器就实现了,希望大家也可以动手,封装自己的装饰器。

欢迎大家留言讨论,祝工作顺利、生活愉快!

我是bigo前端,下期见。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值