-1

A little pseudo code to explain the title:

   Subject source = new Subject();

   function sendInNewResource(newNumber, anotherVariable) {
       source.next(newNumber);   
   }

   const newSource = source
     .map((myNumber) => myNumber++);

   newSource.subscribe((data) => {
       //How can I access myObject here?
   });

   sendInNewResource(1, {myObject});

I think the pseudo code explains a little of what I want to do. So far, I've tried extending the observable, but if I did that it looks as though I'd need to override the operators within the observable.

I almost want to provide metadata to the event.

I do not want to pass the myObject down as this is a library for use externally, I will be subscribing to the observables within my codebase but chaining the observable will be done externally.

Anyone have any ideas on how to achieve this?

For context, I'm trying to create a purely RxJs http library that will allow me to do the following:

const server$ = RxHttpServer(server)

const api$ = server.filter((request) => request.url.startsWith('/api'));

const apiEndpoint$ = api.map(() => ({
   status: 400,
   body: 'this is an api endpoint',
   headers: {
   }
}));

// Inside my library, note the use of the response object that I don't have access to:
apiEndpoint.subscribe((dataToSend) => response.write(dataToSend))

The inner logic and handling of these objects has already been written, just need that response object to send back to.

Thomas Nairn
  • 1,186
  • 8
  • 34
  • My knowledge in RxJS is limited but, wouldn't you have to pass myObject as a parameter to next? `source.next(newNumber, myObject)` if that's not possible you could do some argument deconstruction `source.next({newNumber, myObject})`. – Baruch Jun 02 '17 at 17:10
  • @Baruch, that's the one thing I'm trying to avoid with this post, as I don't want users of the framework to have to pass the response down in for every chain that they make. If you imagine an endpoint calling another endpoint through a flatmap, you've immediately lost the response, or would have to use the selector function everywhere, to me that ruins the usability a little :) – Thomas Nairn Jun 02 '17 at 17:12
  • Would you consider wrapping the `response` object you need in an observable? In that case you can `combineLatest` both the `apiEndpoint$` and the `response$` together and then access those 2 in the callback. – atomrc Jun 02 '17 at 17:33
  • @atomrc, I guess you could do that but I'm not entirely sure how that would look as you'd effectively be creating a new observable for every request – Thomas Nairn Jun 02 '17 at 18:38
  • Where does the response come from exactly? Depending on how it is created you can create a subject and feed every `response` into that subject. – atomrc Jun 02 '17 at 18:51
  • The response comes from Observable.fromEvent(, 'request', (request,response) => {})). I originally had that thought, the problem with that is that if we receive a request on one endpoint that takes a second to respond to, but we receive another on an endpoint that we can respond to instantly, the first observable on that subject will be dealt with first, meaning that the 2 responses will get mixed up. Joy! :) – Thomas Nairn Jun 02 '17 at 19:26
  • Oooh, now I understand what you want to do, I have been there (trying to implement an http driver for cyclejs). Actually, one of my friend succeeded in doing this by storing an `id` on the request object and keeping a global store of all the `responses`. I think you can inspire from him https://github.com/rbelouin/cycle-node-http-driver/blob/master/src/index.ts#L52 – atomrc Jun 02 '17 at 22:01
  • The id would work, just not sure if it's really an option to make the framework viable. – Thomas Nairn Jun 04 '17 at 18:07

1 Answers1

0

You could use a responseFor helper that would take the response metadata and return an observable of response Subjects, one for each HTTP request. The library user can subscribe it and write data to each response subject. While you, in the library, can subscribe to that same response subject and send it back to the client when it gets completed. Something like:

// even better extending the Observable prototype
// const apiEndpoint$ = api$.mapResponseFor(...
const apiEndpoint$ = api$.map(responseFor({
   status: 400,
   body: 'this is an api endpoint',
   headers: {}
}));

apiEndpoint$.subscribe(response => {
  response.next(data)
  response.complete()
})
  • Maybe I'm not understanding what you mean properly, but a Subject of type response wouldn't really work as - if we have a request that takes 2 seconds to respond and another request that can be handled instantly, the first (Slow request) will be handled by the second observable. Again, maybe I'm getting this completely wrong, what would the responseFor method do? :) – Thomas Nairn Jun 04 '17 at 18:12
  • It depends on what the `apiEndpoint$` observer does. If being slow means making some other async call and completing the response after that, it will be fine. The async call will be scheduled, javascript will return immediately from the first observer's code and the next observer will be served. If slow means actual computation, the first observer will block and the second won't be served. But that's always a problem with javascript being single threaded. You'd need some platform specific way of doing concurrency ie you could wrap the processing code in a Web Worker. – Federico Galassi Jun 04 '17 at 22:46
  • Another thing you can do is calling `responseSubject.observeOn(Rx.scheduler.async)` on each response subject. This will make sure that when users do `response.next(data)` it will not be executed synchronously, giving other observers some time to be processed. Though, if the computation to calculate `data` is synchronous and long (like having a sleep in the the code) it will still be blocking the main javascript thread. – Federico Galassi Jun 05 '17 at 14:13