javascript - 如何通过消除双重等待和 DRY 代码进行重构?
问题描述
我怀疑哪个是管理此 Web 应用程序中的许多服务客户端的最佳策略。
就用户设备 RAM 和 Javascript 执行速度(主线程操作)之间的良好折衷而言,“最佳”。
这就是我现在正在做的,这是主文件:
- main.ts:
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
,我可以这样使用它:
- 组件/players.svelte
import { getPlayerService } from "main";
const playerService = await getPlayerService();
const players = await playerService.queryPlayers();
这样,如果PlayerService
不存在,则此时导入并返回,否则返回之前导入并实例化的那个。
由于用户经常这样切换页面,我可以避免这些客户端的突然创建和销毁,对吗?
但是通过这种方式,我使用了我不喜欢使用的全局变量,并且我在每个组件中使用了详细、干燥和长代码。
有没有办法在组件中使用以下代码?
import { playerService } from "main";
const players = await playerService.queryPlayers();
你建议我怎么做?
解决方案
您正在实施的模式是“延迟加载”和“单例”。
您可以有一个服务工厂来实现这些模式并将其用于每个服务:
文件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
推荐阅读
- spring - 两个具有相同 URL 但参数不同的 @GetMapping
- google-cloud-functions - 为什么没有特定于调用 GCP 函数的 IAM 角色?
- typescript - TS 2740 类型“ShadowRoot”缺少“元素”类型中的以下属性:属性、类列表、类名、客户端高度等 63 个
- python - Spotify discord.py api 的 if & else 有问题
- python - Sphinx autoclass 在本地工作,而不是 readthedocs,但其他类和方法在 readthedocs 上工作?
- neural-network - 神经网络 - 训练具有多个条目的 MLP
- json - 如何使用 logstash-logback-encoder 自定义 JSON Logging 的消息属性?
- r - 使用 R 中的 data.table 包从表中生成正确输出的问题
- c# - 如何在 .net 应用程序资源上将后备值设置为英语
- python - 处理后导出的json文件为空