首页 > 解决方案 > 如何通过消除双重等待和 DRY 代码进行重构?

问题描述

我怀疑哪个是管理此 Web 应用程序中的许多服务客户端的最佳策略。

就用户设备 RAM 和 Javascript 执行速度(主线程操作)之间的良好折衷而言,“最佳”。

这就是我现在正在做的,这是主文件:

import type { PlayerServiceClient } from './player.client';
import type { TeamServiceClient } from './team.client';
import type { RefereeServiceClient } from './referee.client';
import type { FriendServiceClient } from './friend.client';
import type { PrizeServiceClient } from './prize.client';
import type { WinnerServiceClient } from './winner.client';
import type { CalendarServiceClient } from './calendar.client';

let playerService: PlayerServiceClient;
export const player = async (): Promise<PlayerServiceClient> =>
    playerService ||
    ((playerService = new (await import('./player.client')).PlayerServiceClient()),
    playerService);

let teamService: TeamServiceClient;
export const getTeamService = (): TeamServiceClient =>
    teamService ||
    ((teamService = new (await import('./team.client')).TeamServiceClient()),
  teamService);

let refereeService: RefereeServiceClient;
export const getRefereeService = (): RefereeServiceClient =>
    refereeService ||
  ((refereeService = new (await import('./referee.client')).RefereeServiceClient()),
  refereeService);

let friendService: FriendServiceClient;
export const getFriendService = (): FriendServiceClient =>
    friendService ||
  ((friendService = new (await import('./friend.client')).FriendServiceClient()),
  friendService);

let prizeService: PrizeServiceClient;
export const getPrizeService = (): PrizeServiceClient =>
    prizeService ||
  ((prizeService = new (await import('./prize.client')).PrizeServiceClient()),
  prizeService);

let winnerService: WinnerServiceClient;
export const getWinnerService = (): WinnerServiceClient =>
    winnerService ||
  ((winnerService = new (await import('./winner.client')).WinnerServiceClient()),
  winnerService);

let calendarService: CalendarServiceClient;
export const getCalendarService = (): CalendarServiceClient =>
    calendarService ||
  ((calendarService = new (await import('./calendar.client')).CalendarServiceClient()),
  calendarService);

// and so on... a lot more...

如您所见,有许多服务客户端。

我使用此代码是因为我认为基于几乎与客户端服务重叠的路由的 Web 应用程序结构更好:

我的意思是,如果播放器从一个页面转到另一个/home页面/players,我可以这样使用它:

import { getPlayerService } from "main";

const playerService = await getPlayerService();
const players = await playerService.queryPlayers();

这样,如果PlayerService不存在,则此时导入并返回,否则返回之前导入并实例化的那个。

由于用户经常这样切换页面,我可以避免这些客户端的突然创建和销毁,对吗?

但是通过这种方式,我使用了我不喜欢使用的全局变量,并且我在每个组件中使用了详细、干燥和长代码。

有没有办法在组件中使用以下代码?

import { playerService } from "main";

const players = await playerService.queryPlayers();

你建议我怎么做?

标签: javascripttypescriptdesign-patternsasync-awaitrefactoring

解决方案


您正在实施的模式是“延迟加载”和“单例”。

您可以有一个服务工厂来实现这些模式并将其用于每个服务:

文件serviceFactory.js

const serviceMap = {};

export function getService(serviceName) {
  return serviceMap[serviceName] ?? (serviceMap[serviceName] = import(serviceName).then(x => new x.default));
}

ECMAScript 模块标准将serviceFactory.js只在应用程序中执行一次代码(无论您导入多少次),因此您可以将单例保存在分配给serviceFactory.js模块的私有顶级变量的映射中。

该服务工厂意味着每个服务都使用如下default关键字导出:

export default class SomeService {
    constructor() {
        // ...
    }

    fetchSomething() {
        // ...
    }
}

然后,使用以下代码在应用程序中的任何地方使用服务:

import { getService } from './serviceFactory.js';

const service = await getService('./services/some.service.js');
const something = await service.fetchSomething();

如果你真的想删除 double await,你可以像这样将它封装在服务工厂中:

const serviceMap = {};

export function getService(serviceName) {
  return serviceMap[serviceName] ?? (serviceMap[serviceName] = resolveService(serviceName));
}

function resolveService(name) {
  const futureInstance = import(name).then(x => new x.default);

  const handler = {
    get: function (target, prop) {
      return function (...args) {
        return target.then(instance => instance[prop](...args));
      }
    }
  }

  return new Proxy(futureInstance, handler);
}

它允许您编写以下代码:

const something = await getService('./services/some.service.js').fetchSomething();

这允许在您需要的确切代码行中加载服务。import如果因为您需要import { playerService } from "main";语法而不打扰您使用静态加载它,您可以在每个服务的一个文件中公开每个服务

export const playerService = getService('./services/player.service.js');

我在这里发布了完整的工作演示:https ://github.com/Guerric-P/lazy-singletons-demo


推荐阅读