Skip to content

Instantly share code, notes, and snippets.

@lifeart
Last active October 16, 2023 13:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lifeart/fbcc7bd8747562aa85d79b42ca991493 to your computer and use it in GitHub Desktop.
Save lifeart/fbcc7bd8747562aa85d79b42ca991493 to your computer and use it in GitHub Desktop.
Lazy Services
class Bar {
doSomething() {
console.log('do something');
}
name: string;
}
type PromisifyProps<T> = {
[P in keyof T]: T[P] extends (...args: infer A) => infer R ? (...args: A) => Promise<R> : Promise<T[P]>;
};
// a function to accept service load using import
// and it should return same type as service but all it's methods and properties should be promises
// we can use this function to make lazy loading of services
// under the hood we use proxy to make all methods and properties to be promises
function lazyService<T extends object>(service: () => Promise<T>): PromisifyProps<T> & {
toSync(): Promise<T>;
} {
let loadedService: T;
const proxy = new Proxy({}, {
get(_, prop) {
return new Promise(async (resolve, reject) => {
if (!loadedService) {
try {
loadedService = await service();
} catch(e) {
reject(e);
}
}
if (prop === 'toSync') {
return resolve(loadedService);
}
const value = Reflect.get(loadedService, prop);
if (typeof value === 'function') {
return resolve((...args: any[]) => Promise.resolve(value.apply(loadedService, args)));
} else resolve(value);
});
},
});
return proxy as PromisifyProps<T> & {
toSync(): Promise<T>;
}
}
class Foo {
bar = lazyService<Bar>(() => import('./bar'));
async onClick() {
// auto-load and invoke service method
await this.bar.doSomething();
// get service property
const name = await this.bar.name;
// convert async service to sync (auto-load)
const sync = await this.bar.toSync();
const secondName = sync.name;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment