Tengo un algoritmo comercial que coloca pedidos según los precios que recibe de un flujo de datos. Cuando está en producción, recibimos precios de un zócalo WS, por lo que es asíncrono de forma natural.
Sin embargo, queríamos probar nuestro algoritmo con precios precargados enviados al algoritmo de forma sincronizada. El problema es que, como el algoritmo es totalmente asíncrono en producción, todas las funciones están marcadas como asíncronas y tienen llamadas en espera. Sin embargo, en el modo de "prueba", todas las funciones asíncronas en realidad devuelven Promise.resolve(whatever) inmediatamente porque todo es falso (los precios son falsos, la realización del pedido es falsa, etc.) y no esperamos nada de Internet.
El problema es que, como todas estas funciones son asíncronas, a pesar de que devuelven inmediatamente un promise.resolve(whatever) , son mucho más lentas que si devolvieran whatever que fuera directamente.
Mi pregunta es: ¿es posible hacer funciones "asyncOrNot" en JS/TS que se llamarían con "awaitOrNot"? Si no, ¿qué enfoque se puede pensar en esta situación para deshacerse de todo el tiempo de sobrecarga asíncrono, para hacer que el entorno "falso" sea más rápido? Me gustaría evitar mantener 2 algoritmos diferentes (uno asíncrono de producción y otro sincronizado con fines de prueba)
Solo un pequeño fragmento de código para entender lo que me gustaría:
abstract class PriceStream { onPriceCallback: (price: number) => PromiseOrNot<void> abstract startStreaming(): void } class RealAsyncPriceStream extends PriceStream { startStreaming() { // this is not real WebSocket functions but it's just for you to understand // that prices are arriving and sent asynchronously to the callback here webSocket.onMessage((message) => { const price = ... // do formatting of message to a price this.onPriceCallback(price) }) webSocket.start() } } class FakeSyncPriceStream extends PriceStream { startStreaming() { const prices = [0.6, 0.8, 0.9, 0.10] for (price of prices) { this.onPriceCallback(price) } } } class Algo { constructor(priceSteam: PriceStream) { priceStream.onPriceCallback = this.onPrice.bind(this) } run() { priceStream.startStreaming() } asyncOrNot onPrice(): PromiseOrNot<void> { // perform business operations that are : // - all asynchronous in production (placing and cancelling order awaits for the broker response) // - all synchronous in fake environment : placing and cancelling orders only locally in memory : all async function return immediately Promise.resolve(...) } }Es una idea interesante. Como dije en el comentario, es posible con una impl de promesa personalizada que sombrea la impl nativa. Debe aplicarlo como polyfill para anular globalThis.Promise .
Con el objetivo de compilación de TS establecido en ES2016 e inferior, el código JS compilado no usará la función de lenguaje async/await . En su lugar, utiliza generador y Promise, que pondrán a trabajar su implementación personalizada.
A continuación se muestra mi impl. Se adapta automáticamente al comportamiento sincronizado y asincrónico, dependiendo de si llama sincrónicamente a la devolución de llamada de resolve/reject al instanciar Promise. No está listo para la producción porque la interfaz no está alineada con las especificaciones. Pero basta para demostrar la idea.
const RealPromise = globalThis.Promise class SyncPromise { static resolve(value) { return new SyncPromise((resolve) => resolve(value)) } constructor(callback) { this.status = 'pending' this.resolve = (value) => { this.status = 'resolved' this.value = value this.run() } this.reject = (value) => { this.status = 'rejected' this.value = value this.run() } this.callbacks = [] try { callback(this.resolve, this.reject) } finally { if (this.status === 'pending') { return new RealPromise((resolve, reject) => { this.callbacks.push([resolve, reject]) }) } } } then(onFulfilled = (x) => x, onRejected = (x) => x) { return new SyncPromise((rs, rj) => { switch (this.status) { case 'pending': break case 'resolved': rs(onFulfilled(this.value)) break case 'rejected': rj(onRejected(this.value)) break } }) } run() { const callbacks = this.callbacks this.callbacks = [] for (let [onFulfilled, onRejected] of callbacks) { if (this.status === 'resolved') { onFulfilled(this.value) } else if (this.status === 'rejected') { onRejected(this.value) } } } } globalThis.Promise = SyncPromise async function main() { let value = await Promise.resolve(42) console.log(value) }