Is subscribing inside RxJS tap, bad practice for an asynchronous logic? - javascript

I am having two http requests where the second one (Log request) should be subscribed after the first one (Order request) emits a value and I do some logic that should not be blocked by the Log request and the result of log request is ignored.
As I understand that tap is
Used to perform side-effects
https://rxjs.dev/api/operators/tap
and since I assume Log request and its response here is a side-effect to the Order request, is it bad practice to subscribe inside tap? Is there a more slick and RxJS way of handling this?
const order = of('2- created order').pipe(
delay(100),
tap(console.log)
);
const log = of('4- logged info to log server').pipe(
delay(500),
tap(console.log)
);
console.log('1- started creating order');
order
.pipe(tap(() => log.subscribe()))
.subscribe(() => console.log("3- didn't wait for log server"));
StackBlitz

Yes, it definitely is bad practice.
You're right that tap is there for side effects but those should not involve other streams, they should be simple side effects like assigning variables or console logging and stuff.
The issue is that you generally do not want to subscribe inside a subscribe on inside the pipe because doing so makes for very unpredictable and difficult to maintain code.
For example your subscribe inside the tab looks innocuous enough, but imagine if you were listening to a continuous stream of data, how many subscriptions would you have? would you care to track them all to unsubscribe, wouldn't that end up quite difficult to understand/debug, etc...
The problem with your code is that you are somewhat thinking in an imperative way (something like "do this and then that, and then...") instead of thinking in terms of streams.
So basically in my opinion, instead of thinking something like "how do I do this before that?" you should be thinking about how you handle the stream and the order of operations you can perform on it.
In your case, is there any reason why you'd want to print the third message inside the subscribe and not in the pipe?
Why not just doing the following?
order
.pipe(
tap(() => console.log("3- didn't wait for log server")),
switchMap(() => log)
)
.subscribe();
(like here: https://stackblitz.com/edit/rxjs-aace8i?file=index.ts)
If I may, I would like to analyse your question...
Starting with
I am having two http requests where the second one (Log request)
should be subscribed after the first one (Order request) emits a value
that just seems a simple case of having an initial observable (Order) and needing to use a mapping operator to move to a different one (Log), I assumed that you want to discard the first and move to the second so I chose switchMap (alternatively you could use concatMap or mergeMap)
Then we get to:
and I do some logic that should not be blocked by the Log request and
the result of log request is ignored.
since we already thought of how to handle the 2 observables, if we read your sentence it really spells out that we just want a side effect to occur between the first and second observable, and it ignores the streams' values anyways, so it clearly calls for a simple tap
I am sorry for the rather long message I hope it does not sound too pedantic :P
What I basically wanted to say is that you should always look at your streams and sort of think how to fit all together in accordance to rxjs programming style and what you need, and not whether some ways of doing something is acceptable or not, as that makes me realy thing that you already had suspicions on whether it not being the best solution.

As #martin mentions in the comment, it's generally bad to subscribe inside a pipe (not specifically tap, but any operator) because there's no way handle cleaning up subscriptions.
Generally, it's preferred to use one of the "Higher-Order Mapping Operators" because they handle subscribing, emitting, and unsubscribing from your observable.
Is there a more slick and RxJS way of handling this
Not sure if this would be considered "slick" or not :-), but I think it gives a nice separation of concerns if a Subject is used as a dedicated stream of log messages; then create a single subscription to have your server logging logic executed:
const logMessage$ = new Subject<string>();
logMessage$
.pipe(mergeMap(logToServer))
.subscribe();
Then in your other code, instead of subscribing, you can call logMessage$.next() to trigger the server logging logic without impeding the flow of your order stream:
order.pipe(tap(o => logMessage$.next(o)));
Here's an updated StackBlitz.

Related

Wait for response from emitted message?

I'm having a trouble wrapping my head around following concept.
I'm sending OSC messages to query status of instruments in Ableton, so I have emmiter/receiver combo going on. Now, thing is that I'd like to avoid having to keep up some sort of global state and wrap everything around this.
and I do communicate with Ableto in following fashion:
sender.emit("/live/device", queryData);
receiver.on("/live/device", function(responseData){
// process response here...
})
So you can tell that I'm not really sure when I got data back and cannot really sequence new queries based on responses.
What I'd like to do is to simply
query number of instruments on ONE certain channel
get number back
query parameters of each instrument of that channel based on first query
receive parameters back
But problem is that I have no idea how to wrap eventListeners to respond to these queries, or rather how to sequence them in way that is non-blocking and yet still avoiding having some sort of global state going on.
Querying data and storing Promises to be resolved by eventListener seems like a solution, but then I'm stuck on how to pass them back to sequence.
After some research, it seems that this kind of behaving breaks the whole concept of event listeners, but then I suppose the whole point is to have some global state to keep track of what is going on, right?
Event listeners are telling you some asynchronous action coming from a user action or any other interrupt. Depending on the API you are facing, they might have re-used event listeners for replies instead of providing a promise or callback return for the send API. If the server has multiple clients interacting with it, it might want to tell all clients at the same time when their state changes as well.
If you are sure there is no way to directly provide a callback in the send method for a reply to your request or a request does not yield a promise that resolves with the reply at some point, there are usually workarounds.
Option 1: Send context, receive it back
There are APIs that allow sending a "context" object or string to the API. The API then sends this context to the event listeners whenever it answers this specific question along with their payload. This way, the context part of their payload can be checked if it's the answer to the request. You could write your own little wrapper functions for a more direct send/reply pattern then.
Option 2: Figure out the result data, if it fits your request
If the resulting data has something specific to match on, like keys on a JSON object, it may be possible to find out what the request was.
Option 3: Use state on your side to keep track of everything
In most cases where I have seen such APIs, the server didn't care much about requests and only sent out their current state if it was changed by some kind of request. The client needs to replicate the state of the server by listening to all events, if it wants to show the current server state.
In most situations where I faced this issue, I thought about Option 1 or 2 but ended up with Option 3 anyways: Other clients or hardware switches might interfere with my client UI and change the server state without me listening on that change. That way I would loose information that invalidates my UI, so I would need to listen and replicate the state of the server/machine/hardware anyways.

Is it safe to assume that the RxJS will trigger the next of each observer in the subscription order?

Is it safe to assume that RxJS will trigger the next function of each of its observers in the order they have subscribed. I have a class with a public propery of BehaviorSubject. The first subscription made to it will be from with in the class' constructor. I would like to make sure that the next of this private subscription works before any other's.
Practically speaking, yes, this is safe; the implementation of the Subject class (from which BehaviorSubject inherits) always processes subscriptions in the order they are taken. While I've not seen a guarantee from the rxjs team that this will always be the case, I imagine changing this would break a lot of code (mine included).
Strictly speaking, no, no guarantee is made regarding subscription processing order. This goes back to Rx under .NET, when the team tried to align the subscription behavior with that of multicast delegates (you can find a good explanation from Bart De Smet at https://social.msdn.microsoft.com/Forums/en-US/ac721f91-4dbc-40b8-a2b2-19f00998239f/order-of-subscriptions-order-of-observations?forum=rx).
I have run across scenarios before where the "process in subscription order" hasn't suited me, and I've needed to take direct control. In this case, I've used a simple function to turn one observable into two, one of which is guaranteed to be notified before the other. You could use a similar method to avoid making the assumption that subscriptions will always be processed in order, though I personally do not think it's necessary. If interested, you can find details here: RxJs: Drag and Drop example : add mousedragstart
In terms of behaviorSubject, and subjects in general, they are "Hot" Observables that produce and consume values. You can assume that the next function will always trigger so long as nothing calls the observer.complete() method.
The first subscription you have will set and initialize the state (assumption here) and so every subsequent subscriber will be able to hook in to that subscription and ascertain the next(value) emissions.
Hope this helps.

When's the best time to use process.nextTick?

The impression I get from people is... All JavaScript functions are synchronous unless used with process.nextTick. When's the best time to use it?
I want to make sure that I don't over use it in places where I don't need it. At this point, I'm thinking to use it right before something like a database call, however, at the same time, as I understand, those calls are asynchronous by default because of the whole "async IO" thing.
Are they to be used only when doing some intensive work within the JavaScript boundaries? Like parsing XML etc?
Btw, there's already a question like this but it seems dead so I raised another one.
I'm thinking to use it right before something like a database call, however, at the same time, as I understand, those calls are asynchronous by default because of the whole "async IO" thing.
Yes. The database driver itself should be natively asynchronous already, so you don't need to use process.nextTick yourself here to "make it asynchronous". The most time-consuming part is the IO and the computations inside the database, so waiting an extra tick just slows things down actually.
Are they to be used only when doing some intensive work within the JavaScript boundaries? Like parsing XML etc?
Yes, exactly. You can use it to prevent large synchronous functions from blocking your application. If you want to parse an XML file, instead of gnawing through it for 3 seconds during which no new connections can be opened, no requests received, and no responses be sent, you would stream the file and parse only small chunks of it every time before using nextTick and allowing other work to be done concurrently.
However, notice that the parser should use nextTick internally and offer an asynchronous API, instead of the caller using nextTick before invoking the parser.
This answer makes no claims of being complete, but here are my thoughts:
I can imagine two use cases. The first one is, to make sure something is really async. This comes in handy when using EventEmitter. Imagine you want to be able to use all methods of your emitter like this:
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {
aMethod(){
console.log('some sync stuff');
this.emit('aMethodResponse');
return this;
}
}
var myEmitter = new MyEmitter();
myEmitter.aMethod()
.once('aMethodResponse', () => console.log('got response'));
This will simply not work as the event is fired before the listener is established. process.nextTick() makes sure that this won't happen.
aMethod(){
console.log('some sync stuff');
process.nextTick(() => this.emit('aMethodResponse'));
return this;
}
Edit: removed second suggestion because it was simply wrong

What's the idiomatic way to keep UI in sync with ajax calls when using Flux?

The general problem: Let's say I have a button with an onClick handler calling an action creator. The action does an ajax call which dispatches a message when ajax responds, and this in some way affects the UI. Given this basic pattern there's nothing stopping the user from clicking this button multiple times, and thus running the ajax call multiple times.
This is something that doesn't seem to be touched upon in the React or Flux documentation (as far as I have seen), so I've tried to come up with some methods on my own.
Here are those methods
Use lodash.throttle on a method which does an ajax call so that multiple clicks in quick succession don't create multiple calls.
Use lodash.debounce on a method so that ajax is only called once a user hasn't done any activity for a bit. This is how I'm doing semi-realtime updates of text fields on change.
Dispatch an "is updating" message to stores when the action is first called and then dispatch a "done" message when the ajax call returns. Do stuff like disabling input on the initial message and then re-enable on the second.
The third method seems to be the best in terms of functionality since it allows you to make the user interface reflect exactly what's going on, but it's also incredibly verbose. It clutters absolutely everything up with tons of extra state, handler methods, etc...
I don't feel like any of these methods are really idiomatic. What is?
Hal is pretty much correct. Dispatching multiple messages is the Fluxiest way to go.
However, I would be wary of dispatching an IS_UPDATING message. This makes reasoning about your code harder because for each AJAX action you're dispatching several actions at once.
The idiomatic solution is to split your AJAX "actions" (action-creator-actions) into three dispatched actions: MY_ACTION, MY_ACTION_SUCCESS, MY_ACTION_FAILURE, handling each instance appropriately, and tracking "pending-ness" along the way.
For example:
// MyActionCreator.js
// because this is in a closure, you can even use the promise
// or whatever you want as a sort of "ID" to handle multiple
// requests at one time.
postMessage() {
dispatch('POST_MESSAGE', { ... } );
api.slowMessagePostingAjaxThingy().then(
(success) => { dispatch('POST_MESSAGE_SUCCESS', { ... }); },
(failure) => { dispatch('POST_MESSAGE_FAILURE', { ... }); }
);
}
// MyStore.js
on('POST_MESSAGE', (payload) => { /* do stuff */ });
on('POST_MESSAGE_SUCCESS', (payload) => { /* handle success */ });
on('POST_MESSAGE_FAILURE', (payload) => { /* handle failure */ });
This gives you several benefits over your alternate solutions:
Your store is exclusively in control of whether an item is pending or not. You don't have to worry about changing UI state on actions in your UI code: you can have your UI look exclusively to a pending property of your store for truth. This is probably the biggest reason for using Flux over MVC systems.
You have a clean interface for taking your actions. It's easy to reason about and easy to attach other stores to this data (if you have a LatestMessageStore or something, it's easy to subscribe to these events). This is the benefit over using IS_UPDATING as Hal suggested.
You save your lodash calls for when they semantically make sense— like when you may be inundated with legitimate data (a text field).
You can easily switch between optimistic updates (change the store when POST_MESSAGE is called) or pessimistic updates (change the store on POST_MESSAGE_SUCCESS).
I would argue that the third method is the correct way, but I don't find it to be verbose. A lot of React code that I see written sort of misses the spirit of React with its idea of very small, composable components. When large monolithic components are created, yes, things can get very messy.
But if the button in question is its own component, then it can take care of rendering based on its state. When a user clicks the button, the state of just that component changes -- and it renders it in a way that it can't be clicked again.
Once the store has notified that component that it has changed, the component can set its state back -- and with it, re-render itself.
It's a pretty straight-forward process; it just requires thinking about pages as a collection of small, composable units.

Forever loop while waiting for asynchronous task?

I'm wondering if there's a way to cause JavaScript to wait for some variable-length code execution to finish before continuing using events and loops. Before answering with using timeouts, callbacks or referencing this as a duplicate, hear me out.
I want to expose a large API to a web worker. I want this API to feel 'native' in the sense that you can access each member using a getter which gets the information from the other thread. My initial idea was to compile the API and rebuild the entire object on the worker. While this works (and was a really fun project), it's slow at startup and cannot show changes made to the API without it being sent to the worker again after modification. Observers would solve part of this, and web workers transferrable objects would solve all, but they aren't adopted widely yet.
Since worker round-trip calls happen in a matter of milliseconds, I think stalling the thread for a few milliseconds may be an alright solution. Of course I would think about terminating in cases where calls take too long, but I'm trying to create a proof of concept first.
Let's say I want to expose the api object to the worker. I would define a getter for self.api which would fetch the first layer of properties. Each property would then be another getter and the process would continue until the final object is found.
worker.js
self.addEventListener('message', function(event) {
self.dataRecieved = true;
self.data = event.data; // would actually build new getters here
});
Object.defineProperty(self, 'api', {
get: function() {
self.dataRecieved = false;
self.postMessage('request api first-layer properties');
while(!self.dataRecieved);
return self.data; // whatever properties were received from host
}
});
For experimentation, we'll do a simple round-trip with no data processing:
index.html (only JS part)
var worker = new Worker("worker.js");
worker.onmessage = function() {
worker.postMessage();
};
If onmessage would interrupt the loop, the script should theoretically work. Then the worker could access objects like window.document.body.style on the fly.
My question really boils down to: is there a way to guarantee that an event will interrupt an executing code block?
From my understanding of events in JavaScript, I thought they did interrupt the current thread. Does it not because it's executing a blank statement over and over? What if I generated code to be executed and kept doing that until the data returned?
is there a way to guarantee that an event will interrupt an executing code block
As #slebetman suggests in comments, no, not in Javascript running in a browser's web-worker (with one possible exception that I can think of, see suggestion 3. below).
My suggestions, in decreasing order of preference:
Give up the desire to feel "native" (or maybe "local" might be a better term). Something like the infinite while loop that you suggest also seems to be very much fighting agains the cooperative multitasking environment offered by Javascript, including when thinking about a single web worker.
Communication between workers in Javascript is asynchronous. Perhaps it can fail, take longer than just a few milliseconds. I'm not sure what your use case is, but my feeling is that when the project grows, you might want to use those milliseconds for something else.
You could change your defined property to return a promise, and then the caller would do a .then on the response to retrieve the value, just like any other asynchronous API.
Angular Protractor/Webdriver has an API that uses a control flow to simulate a synchronous environment using promises, by always passing promises about. Taking the code from https://stackoverflow.com/a/22697369/1319998
browser.get(url);
var title = browser.getTitle();
expect(title).toEqual('My Title');
By my understanding, each line above adds a promise to the control flow to execute asynchronously. title isn't actually the title, but a promise that resolves to the title for example. While it looks like synchronous code, the getting and testing all happens asynchronously later.
You could implement something similar in the web worker. However, I do wonder whether it will be worth the effort. There would be a lot of code to do this, and I can't help feeling that the main consequence would be that it would end up harder to write code using this, and not easier, as there would be a lot of hidden behaviour.
The only thing that I know of that can be made synchronous in Javascript, is XMLHttpRequest when setting the async parameter to false https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#Parameters. I wonder if you could come up with some sort of way to request to the server that maintains a connection with the main thread and pass data along that way. I have to say, my instinct is that this is quite an awful idea, and would be much slower than just requesting data from the main thread.
For what I know, there is not something native in JS to do this but it is relatively easy to do something similar. I made one some time ago for myself: https://github.com/xpy/whener/blob/master/whener.js .
You use it like when( condition, callback ) where condition is a function that should return true when your condition is met, and callback is the function that you want to execute at that time.

Categories