tl; dr: ¿cómo hacer una versión de una clase que tiene todos sus métodos asíncronos reemplazados por los de sincronización? (o viceversa)
Información de fondo:
He creado una clase base abstracta usando símbolos, algo como esto:
const sProtectedMethod = Symbol(); class AbstractBase() { constructor() { if (new.target === AbstractBase) { throw new TypeError(`AbstractBase is abstract and cannot be instantiated directly. Use a derived class`); } } async #protectedMethod() { // this is just a convience to avoid the ugly symbol syntax everytime we call the function await this[sProtectedMethod](); } async publicMethod() { await longAndComplexFunctionCallChainThatEventuallyAwaitsProtectedMethodButDoesntAwaitAnythingElse(); }donde las clases derivadas se ven así:
class Derived extends AbstractBase { async [sProtectedMethod]() { // implementation here }Hasta aquí todo bien. Pero, en algunas clases derivadas, la implementación del método anulado debe ser asíncrono (porque hace E/S o lo que sea), mientras que en otras implementaciones este método podría ser síncrono. Hice un experimento para ver si sincronizar el método sería más rápido y es - aprox. 1000 veces más rápido.
Podría hacer 2 versiones de AbstractBase: una versión sincronizada y una asíncrona. Pero aquí está el problema: el método protegido en realidad se llama muchas veces, bastante profundo en una larga cadena de funciones, todas las cuales deben ser asíncronas si la implementación anulada también es asíncrona.
Entonces, en lugar de mantener 2 versiones de la clase que son casi idénticas, me gustaría una forma de crear automáticamente una versión sincronizada a partir de una versión asíncrona, o viceversa.
He logrado esto con un reemplazo de cadena y un eval
const AbstractBaseSync = eval(`${AbstractBase.toString().replace(/async|await/g, ``)}; AbstractBase`);Pero aunque creo que esto es seguro (siempre que no haya cadenas dentro de la clase que contengan las palabras async o await), el uso de eval y espero que haya una mejor manera.
Además, aunque parece funcionar en Chrome, Firefox + Node: sé que las implementaciones de toString pueden diferir, lo que podría causar problemas más adelante.
Si prescindiera de los miembros #privados y usara _convention en su lugar, entonces tal vez podría hacerlo miembro por miembro con reflexión, pero no creo que sería mucho mejor ya que todavía se requeriría eval o new Function(...) .
¿Cuál es la forma más limpia de hacer esto?