Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

366
Views
TS/JS : conditional async function?

I have a trading algorithm placing orders depending on prices it receives from a stream of data. When in production, we receive prices from a WS socket, so it is naturally asynchronous.

Though, we wanted to test our algorithm with pre-loaded prices sent to the algorithm synchronisly. The problem is that, as the algorithm is fully asynchronous in production, all function are marked async and have await calls in them. Though, in the "test" mode, all that async functions actually return Promise.resolve(whatever) immediately because everything is fake (prices are fake, placing order is fake etc.) and we don't wait for anything from the Internet.

The problem is that, as all these functions are asynchronous, despite they return immediately a promise.resolve(whatever), they are much slower that if they returned whatever directly.

My question is : is it possible to make "asyncOrNot" functions in JS/TS that would be called with "awaitOrNot" ? If not, what approach can be thought of in this situation to get rid of all the async overhead time, to make the "fake" environment faster ? I'd like to avoid to maintain 2 different algorithms (one production async one and another sync one for test purposes)

Just a little piece of code to understand what I'd like :

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(...)
    }
}
about 4 years ago · Santiago Trujillo
1 answers
Answer question

0

It's an interesting idea. Like I said in comment, it's possible with a custom promise impl that shadows the native impl. You need to apply it as polyfill to override globalThis.Promise.

With TS compile target set to ES2016 and lesser, compiled JS code will not using async/await language feature. Instead it uses generator and Promise, which will put your custom impl to work.

Below is my impl. It automatically adapts to sync and async behavior, depending on whether you synchronously call the resolve/reject callback when instantiating Promise. Not production ready because interface is not aligned to spec. But enough to demonstrate the idea.

TS Playground

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)
}

about 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!