Javascript 如何通过消除双重等待和干代码来重构?

Javascript 如何通过消除双重等待和干代码来重构?,javascript,typescript,design-patterns,async-await,refactoring,Javascript,Typescript,Design Patterns,Async Await,Refactoring,我怀疑哪种策略是管理此web应用程序中的众多服务客户端的最佳策略 就用户设备RAM和Javascript执行速度(主线程操作)之间的良好折衷而言,“最佳”。 这就是我现在正在做的,这是主文件: main.ts: 这样,如果PlayerService不存在,则此时导入并返回该服务,否则返回之前导入并实例化的服务 由于用户经常以这种方式切换页面,我可以避免突然创建和破坏这些客户端,对吗 但是通过这种方式,我使用了我不喜欢使用的全局变量,并且在每个组件中使用了冗长、枯燥和长代码 是否有办法在组件中

我怀疑哪种策略是管理此web应用程序中的众多服务客户端的最佳策略

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

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

  • main.ts:
这样,如果
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
模块的私有顶级变量的映射中保存单例

此服务工厂意味着导出每个服务时都会使用
默认值
关键字,如下所示:

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

    fetchSomething() {
        // ...
    }
}
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);
}
然后,将应用程序中的所有服务与以下代码一起使用:

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

const service = await getService('./services/some.service.js');
const something = await service.fetchSomething();
const something = await getService('./services/some.service.js').fetchSomething();
如果确实要删除双
wait
,可以将其封装在服务工厂中,如下所示:

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

    fetchSomething() {
        // ...
    }
}
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);
}
它允许您编写以下代码:

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

const service = await getService('./services/some.service.js');
const something = await service.fetchSomething();
const something = await getService('./services/some.service.js').fetchSomething();
这允许在您需要的代码行加载服务。 如果因为需要从“main”中导入{playerService}而不需要加载静态的
import
,那么就可以使用它语法,您可以在每个服务一个文件中公开这样的每个服务

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

我已经在这里发布了完整的工作演示:

我认为@Guerric的代码不能与构建工具(如webpack)一起工作 具体而言,不支持动态字符串导入
导入(modulePath)

我的建议是将代码的重复位减少到它们的最小表示形式。。。希望它最终会感觉不那么吵闹

解决方案#1/2 下面是一个使用高阶
memoize
函数帮助缓存的示例

// Minimal definition of service loaders
export const getPlayerService = memoize<PlayerServiceClient>(async () => new (await import('./player.client')).PlayerServiceClient());
export const getTeamService = memoize<TeamServiceClient>(async () => new (await import('./team.client')).TeamServiceClient());
export const getRefereeService = memoize<RefereeServiceClient>(async () => new (await import('./referee.client')).RefereeServiceClient());
export const getFriendService = memoize<FriendServiceClient>(async () => new (await import('./friend.client')).FriendServiceClient());
export const getPrizeService = memoize<PrizeServiceClient>(async () => new (await import('./prize.client')).PrizeServiceClient());
export const getWinnerService = memoize<WinnerServiceClient>(async () => new (await import('./winner.client')).WinnerServiceClient());


// Mock hacked together memoize fn
// TODO: Replace with some npm library alternative
const fnCache = new WeakMap();
function memoize<TReturn>(fn): TReturn {
  let cachedValue = fnCache.get(fn);
  if (cachedValue) return cachedValue;
  cachedValue = fn();
  fnCache.set(fn, cachedValue);
  return cachedValue;
}
调用服务包装器
你给了我深刻的见解。你的代码很好用。但不幸的是,我发现很难使用它,因为默认情况下服务不会导出,而且这样我就失去了在所有方法中都需要的Typescript的宝贵帮助。我正在使用great从protobuf文件生成我的Typescript服务。您可以为每个服务创建一个
接口
,这样可以在不导入服务类的情况下在整个应用程序中键入服务。它已经为每个服务生成了接口。如何使用它?只需在每个服务“facade”上导入它们,并像这样使用它们:
export const playerService:IPlayerService=getService('./services/player.service.js')确定。那么默认问题呢?哇,米洛万·托马塞维奇,回答得很好!谢谢