• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

和lt;静默重登和gt;为例, 和lt;洋葱模型间件和gt;

武飞扬头像
Bolt
帮助5

藏好自己(内部实现), 做好清理(内存垃圾)

-- 『三体』

洋葱模型

学新通

重登逻辑设计

背景说明

  • 静默登录: 在小程序中, 调用 wx.login() 并与后端通讯拿到登录凭证的过程是对用户无感知的.

  • 静默重登: 由于登录时静默的, 因此接口登录失败时, 也可以在无报错的情况下重新登录后再次执行网络请求.

需求整理

  • 重新登录后要能够再次触发请求

  • 多个接口同时登录失败, 执行登录的方法只应当触发一次, 并对这些接口都再次触发请求.

  • 业务层调用 api.xxx() 对重登无感知, 保持 .then(() => ...) 的写法仍能获取到数据.

设计方案

  • 因为未登录或登录超时情况下都会执行静默重登, 因此可以使用 boolean 标记是否已登录

  • 多个请求同时登录失败, 需要对登录方法加锁, 我们可以直接使用 "登录的 promise 是否存在"

学新通

基于中间件的伪代码

为了将中间件抽象出来, 我们将平台依赖/业务依赖的

  • login - 执行登录

  • checkLoginError - 判断错误是否为登录报错

作为高阶函数入参

fetcher 作为 callback 表示真正执行的网络请求

export const createLoginMiddleware = ({ login, checkLoginError, retryNum = 3 }: CreateOption) => {
  let loggedIn = false;
  let loadingPromise: Promise<unknown> | undefined;
  return defineMiddleware(async (option, fetcher) => {
    async function loginWrapper() {
      if (loggedIn) return;
      if (!loadingPromise) {
        loadingPromise = login?.();
      }
      await loadingPromise;
      loggedIn = true;
      loadingPromise = undefined;
    }
    async function retryFn(n) {
      try {
        await loginWrapper();
        return await fetcher();
      } catch (e) {
        if (n > 0 && checkLoginError?.(e)) {
          loggedIn = false;
          return retryFn(n - 1);
        } else {
          throw e;
        }
      }
    }
    return retryFn(retryNum);
  });
};

洋葱模型原理

自然而然的 callback

如果 fetch 前需要做其它的处理, 我们将

return defineMiddleware(async (option, fetcher) => {

改成

return defineMiddleware(async (option, ``next``) => {

就是我们所说的中间件了

假设除了上述中间件, 我们还有 middlewareX 和 middlewareY, 要使用上述中间件, 最简单的用法:

const option;
const response =
  createLoginMiddleware({
    login,
    checkLoginError
  })(option, () => {
    return middlewareX(option, () => {
      return middlewareY(option, () => {
        ... return axios.get(...);
      })
    })
  })
JS 经典: 回调地狱学新通 学新通

洋葱模型的核心: compose

github.com/koajs/compo…

'use strict'

/**
 * Expose compositor.
 */

module.exports = compose

/**
 * Compose `middleware` returning
 * a fully valid middleware comprised
 * of all those which are passed.
 *
 * @param {Array} middleware
 * @return {Function}
 * @api public
 */

function compose (middleware) {
  if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
  for (const fn of middleware) {
    if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
  }

  /**
   * @param {Object} context
   * @return {Promise}
   * @api public
   */

  return function (context, next) {
    // last called middleware #
    let index = -1
    return dispatch(0)
    function dispatch (i) {
      if (i <= index) return Promise.reject(new Error('next() called multiple times'))
      index = i
      let fn = middleware[i]
      if (i === middleware.length) fn = next
      if (!fn) return Promise.resolve()
      try {
        return Promise.resolve(fn(context, dispatch.bind(null, i   1)))
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}

使用 compose 解决 callback

class LoginError extends Error {}

const run = compose([
  createLoginMiddleware({ login, checkLoginError })
  middlewareX,
  middlewareY,
  处理报错中间件 // 判断后端返回的报错是否为登录报错, 并 throw LoginError
              // 这样错误类型就可以内聚在使用层
]);

const response = run(option, () => {
  return fetch(...);
});

已知问题:

koa 的 compose 实现, 对于中间件中的 next 方法只允许调用一次, 并不适用于上述重登逻辑. 我们只需要简单修改代码就可以了.

告诉大家一个小秘密: 上面的中间件伪代码就是真实可运行的.

用伪代码来描述设计思路, 将需要隐藏的细节定义为函数, 最后再通过入参实现函数本身.

竞品对比

axios 中间件处理

axios.interceptors.request.use((config) => {
  console.log('interceptors.request1');
  return config;
}, (error) => {
  return Promise.reject(error);
});
axios.interceptors.request.use(...);
axios.interceptors.request.use(...);
axios.interceptors.response.use(...);
axios.interceptors.response.use((config) => {
  console.log('interceptors.request2');
  return config;
}, (error) => {
  return Promise.reject(error);
});

axios 的中间件是将 request 和 response 分开处理的.

通过伪代码的实现, 需要再进行一次逻辑封装, 才能避免散落在多个 interceptors 中.

而一个中间件的逻辑需要足够的内聚, 减少其它同学的理解成本, 提高可维护性.

作为分享后习题, 请大家基于 axios 中间件实现重登机制.

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfgaejh
系列文章
更多 icon
同类精品
更多 icon
继续加载