社群接口code码治理实录

113 阅读6分钟

一、技术债务

因为早期的技术设计和妥协(鄙人猜测),需要前端侧在接口调用处理时兼顾两层code码的判断,导致前端项目中既有一层code码又有两层code码的处理,形成了编码思想上的不统一,会对继任的开发者造成极大困扰(什么时候是一层code码,什么时候又是两层code码?)。为了统一编码思想和偿还技术债务,需前端侧与应用层服务端、网关侧合力开发解决这个问题。

当回事后诸葛亮,若是早期的开发者能够前瞻假设下或坚持己见就不会有今天这档事;虽然有些问题可以在前端很容易处理掉,但是也的思考下这么做的代价。

二、设计方案

前端侧不参与是单层code结构还是双层code结构的逻辑计算,主要负责梳理接口结构、剥离双层code结构逻辑和完善接口调用的处理。网关侧针对社群业务增加相关逻辑,如果http响应头里有社群标识且响应体包含code信息就直接返回给前端侧。服务端负责在响应头上增加社群标识。

三、开发经历

  • 调用的服务端接口很多,有没有统一的解决方案?

    • 没有。前端侧没有针对双层code结构的中间层统一处理方式,所有的接口处理都在各自的页面组件里。
  • 调用的接口又多,又没有统一的解决方案,那该怎么办呢?

    • 凉办。梳理统计所有的业务板块,然后按人头分配下去,人肉去甄别处理。有时笨办法就是最有效的办法。
  • 为了不影响正在测试的需求,代码不能暂时不能部署到sit环境,怎么办呢?

    • 网关和服务端代码部署到dev环境。
  • dev环境数据不全,不便调试验证,还得部署到sit环境,怎么办呢?

    • 网关和服务端代码部署到sit环境。
  • 万一来了紧急需求,测试需要在sit环境验证,怎么办呢?

    • 部署原来网关和服务端代码部署到sit环境。
  • 来来回回部署网关和服务端代码部署到sit环境,是不是有点儿难为兄弟们了?

    • 加开关。据设计方案可知,是返回单层code结构还是双层code结构是有社群服务端控制的,故可以加个开关切回原来的逻辑。
  • 治理后的接口返回的数据较之前返回了多个值为null的无用数据,是怎么回事呢?

    • 网关序列化问题。之前网关包装服务端接口时会进行序列化处理,序列化会把null的字段去掉。
  • 治理后的接口返回异常code码(非0或非200)时出现了两个toast提示,是什么情况?

    • 在接口响应层有一处封装,当code码非0或非200时会抛出个错误异常,该异常会被Promise的catch方法捕获并在此做了次toast提示然后return false返回,下游业务应用层接着消费这个Promise.resolve(false)Promise再次做了toast提示。
  • 那之前为什么不曾出现两次的toast提示呢?

    • 运气好。因为之前网关侧做了一层包装且很少返回异常code码,基本上不会抛出异常触发Promise的catch方法,相关的toast提示都在业务层完成。
  • 有没有什么办法可以试着屏蔽(或保留)一层toast提示呢?

    • 没有。删除catch方法的toast提示?不行,下游有的业务层利用抛异常的方式来弹toast提示,不能直接删;catch方法返回Promise.reject()让下游业务不走.then方法来阻断业务层的toast提示?不行,下游业务层没有对异常进行兜底处理是要报错的。
  • 到底怎么办才能保留一层toast提示呢?

    • 头疼医头脚疼医脚。哪里有问题解决哪里的问题,莫要为了解决该问题引入其他问题。
  • 万一项目上线后出现问题怎么办呢?

    • 前端立即回滚,后端立即回滚或打开对应的开关;如此可以较少对网关的依赖。
    • 利用测试环境观察时间,大家在测试环境充分验证。

四、温故知新

  1. Promise的使用

    1.1 以为.catch(failureCallback)是个特殊的方法,其实它等价于.then(null, failureCallback)
    1.2 以为已经settled的promise再调用.catch().then()方法不会执行,其实它会执行。
    1.3 以为promise的任务编排只能这样.then().then().catch(),其实它可以这样

       Promise.reject('oops!')
         .then(null, res => {
           console.log(res);
           return 'yes!';
         })
         .finally(() => {
           console.log('finally');
           throw 'no!'
         })
         .catch(err => {
           console.log(err, 'error');
         })
         .then(res => {
           console.log(res, 'last-then');
         })
         .finally(() => {
           console.log('last-finally');
         })
         .catch(err => {
           console.log(err, 'last-err');
         });
         
       const promiseA = new Promise((resolutionFunc, rejectionFunc) => {
          resolutionFunc(777);
       });
    
       promiseA.then((val) => { 
         console.log("asynchronous logging has val:", val); 
         return 12;
       }).then(res=>{
       console.log(res);
       });
       promiseA.then((val) => console.log("asynchronous logging has val:", val));
       console.log("immediate logging");
    
       // produces output in this order:
       // immediate logging
       // asynchronous logging has val: 777
       // 12 
    

    1.4 promise的.then中回调函数具体的返回值依据以下规则返回: > - 返回了一个值,那么 then 返回的 Promise 将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。 > - 没有返回任何值,那么 then 返回的 Promise 将会成为接受状态,并且该接受状态的回调函数的参数值为 undefined。 > - 抛出一个错误,那么 then 返回的 Promise 将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。 > - 返回一个已经是接受状态的 Promise,那么 then 返回的 Promise 也会成为接受状态,并且将那个 Promise 的接受状态的回调函数的参数值作为该被返回的 Promise 的接受状态回调函数的参数值。 > - 返回一个已经是拒绝状态的 Promise,那么 then 返回的 Promise 也会成为拒绝状态,并且将那个 Promise 的拒绝状态的回调函数的参数值作为该被返回的 Promise 的拒绝状态回调函数的参数值。 > - 返回一个未定状态(pending)的 Promise,那么 then 返回 Promise 的状态也是未定的,并且它的终态与那个 Promise 的终态相同;同时,它变为终态时调用的回调函数参数与那个 Promise 变为终态时的回调函数的参数是相同的。

  2. 不要试图使用统一处理的思想处理那些不统一的事物,让它们待在各自该待的地方就好。

以上代码试图通过抛出异常来触发promise的.catch中回调函数来达成统一处理错误信息提示的功能,给后来code码治理带来了极大困扰。

五、其他优化

  1. 编码过程中,修复了一部分TS类型错误问题。
  2. 调整了webpack打包方式,对chunks包进行了瘦身。