How can I refactor for handling multiple proto generated service clients by eliminating double await and the DRY code?
Angular
If you use Angular you are able to use store. For example ngxs or ngrx modules for your application.
One more way in angular use resolvers.

Interface that classes can implement to be a data provider. A data
provider class can be used with the router to resolve data during
navigation. The interface defines a resolve() method that is invoked
when the navigation starts. The router waits for the data to be
resolved before the route is finally activated.

//player.resolver.ts
class PlayerResolver implements Resolve&lt;number&gt; {
   private req = false;
   private repSubject = new ReplaySubject(null);

   constructor(private service: PlayerServiceClient) { }

   public resolve(route: ActivatedRouteSnapshot): Observable {
        if (!this.req) {
            this.req = true;

            this.service.get().subscribe(res =&gt; this.repSubject.next(res));
        }
        return this.subject.pipe(first());
    }
}

//app-routing.module.ts
const routes: Routes = [
...
    {
     path: 'player'
     resolve: {data: PlayerResolver}
    },
    {
     path: 'friend'
     resolve: {data: FriendResolver}
    }
]

ReactiveX
If you use RxJs in your app, you would to do caching, create BaseSender:
//base.sender.ts
let hash = require('object-hash');

class BaseSender {

  ...
  protected httpSenderWithCache(service: Observable&lt;any&gt;,param): Observable&lt;any&gt; {
    const id = hash(param);

    if (!this.cache$[id]) {
      this.expireCache(id, time);
      this.cache$[id] = service.pipe(
        shareReplay(1), // last value
        catchError((err) =&gt; console.log(err))
      );
    }

    return this.cache$[id];
  }

}

shareReplay(1) gets last result.  const id = hash(param); generate unique hash for caching.
//player.service.ts
export class PlayerService extends BaseSender {
   ....
   public get(param = null): Observable&lt;any&gt; {
    param = param || {};

    const data = {
        url: 'https://example.com/player',
        options: {
          params: param,
        },
      };
    const request$ = this.http.get(data.url, data.options);

    return this.httpSenderWithCache(request$, data);
  }
}

General
If you don't use it all.

Install and configire inversify-binding-decorators with decorators for DI in app. In main.ts create container.

//main.ts
    import { injectable, Container } from &quot;inversify&quot;;
    import { provide, buildProviderModule } from &quot;inversify-binding-decorators&quot;; 
    import &quot;reflect-metadata&quot;;

    var container = new Container();
    // Reflects all decorators provided by this package and packages them into 
    // a module to be loaded by the container
    container.load(buildProviderModule());


Create interface ClientServiceInterface

//client-service.interface.ts
interface ClientServiceInterface {
  public get(param = null);
}


Create services

    @provide(PlayerServiceClient)
    export class PlayerServiceClient implements ClientServiceInterface {
    
      private result;
    
      public function get() {
         if(!result) {
           return fetch('https://ex.com/player')
                   .then(res =&gt; {
                      this.result = res;
                      return Promise.resolve(res) 
                    })
         }
         return Promise.resolve(result);  
      }
    }


in your components you have to inject service


  class PlayerComponent {
       constructor(private playerService: PlayerServiceClient) {
         playerService.get().then(...)
       }
    }