I am learning the rxjs and playing with observable and subscribe. I have the following method in component.ts file which returns true/false from API
this.http.get(apiUrl+"/actionName")
.subscribe(result=>
{
if(result){
//step1
//1.show success message
//2.call the other method
//3.and after returned from here
}else{// not true
//1. show error message
//2. returned from here
}
});
});
//step2
// call another function
}
Whenever I subscribe to an observable it immediately jumps to the next line i.e. step 2 and another method gets called first. Which I don't want to do.
I want to run step1 first until it gets finished completely only then it should go to step2.
Thank you in advance.
You don't say so in your question, but I suspect your
//2.call the other method
line contains a nested subscription or a promise. If that's the case, of course your synchronous code will be run before your asynchronous code is run. JavaScript is a single-threaded environment, so you can never wait for other code to run.
Instead, use RxJS various operators to manage the order of your code for you. How you want to do that depends on what you're doing, though sadly call the other method isn't descriptive enough.
Assuming theOtherMethod and anotherFunction are actually strangely named observables, then you might do something like this:
this.http.get(apiUrl+"/actionName").pipe(
switchMap(result => {
if(result){
return theOtherMethod;
}
return of("There's no result")
}),
switchMap(otherMethodResult => anotherFunction)
).subscribe(anotherFunctionResult => {
/* Ignore result?*/
});
Related
I'm new to NGXS and I'm trying to fully understand the docs so I can start using it knowing what I'm doing.
There is one thing I don't understand in this code snippet from here.
export class ZooState {
constructor(private animalService: AnimalService) {}
#Action(FeedAnimals)
feedAnimals(ctx: StateContext<ZooStateModel>, action: FeedAnimals) {
return this.animalService.feed(action.animalsToFeed).pipe(tap((animalsToFeedResult) => {
const state = ctx.getState();
ctx.setState({
...state,
feedAnimals: [
...state.feedAnimals,
animalsToFeedResult,
]
});
}));
}
}
Just below this code, it says:
You might notice I returned the Observable and just did a tap. If we
return the Observable, the framework will automatically subscribe to
it for us, so we don't have to deal with that ourselves. Additionally,
if we want the stores dispatch function to be able to complete only
once the operation is completed, we need to return that so it knows
that.
The framework will subscribe to this.animalService.feed, but why?
The action, FeedAnimals, uses the injected service, AnimalService to feed the animals passed in the action's payload. Presumably the service is operates asynchronously and returns an Observable. The value of that Observable is accessed via the tap function and is used to update the ZooState state context based on completing successfully.
In order to use NGXS specifically and Angular in general, you really have to understand RxJS... here's my goto doc page for it
I am using the following code to make get request:
makeGetReq$(url:string):Observable{
let getReqObservable;
getReqObservable = this.httpClient.get(url) //code for making get request
return getReqObservable
}
The problem is sometimes my backend might return {error:true, message} with status code 200. (I know thats weird).In that case I want to intecept getReqObservable and not allow its subscription callback to run.
image.component.ts
makeGetReq$(url:string):Observable{
let getReqObservable;
getReqObservable = this.httpClient.get(url)//code for making get request
return getReqObservable
.do((value)=>{
if(value.error){
//do not allow it to propagate further
})
})
You should propagate it further, but as an error rather than an event (i.e. do just like if your backend did the right thing and returned an error response):
makeGetReq$(url: string): Observable<Something> {
return this.httpClient.get<Something>(url).pipe(
mergeMap(value => value.error ? throwError(value) : of(value))
);
}
Otherwise, the calling method has no way to know that an error occurred, and thus can't execute the callbacks it might have registered for errors.
The easiest would probably be filter.
Filter items emitted by the source Observable by only emitting those that satisfy a specified predicate.
It would look like this:
return getReqObservable
.filter(value => !value.error)
It was pointed out, that you lose the notification completely if you just filter out the error case. There is of course the option to create a RxJS error notification with throwError, but it is also possible to just subscribe to the same source observable a second time with a different filter condition.
Be careful however to not call the backend twice, e.g. by using share.
I have some event Samevent.
For example my event have two listener;
$result = event(new Samevent());
I must be check result;
1 case
FirstListener return false;
SecondListener return false;
dd($result) = [];
2 case
FirstListener return false;
SecondListener return true;
dd($result) = [];
3 case
FirstListener return true;
SecondListener return false;
dd($result) = [true];
4 case
FirstListener return true;
SecondListener return true;
dd($result) = [true, true];
Why happens this?? How can I fix it
The reason everyone here is saying this is a mis-use of Events and Listeners is because their whole purpose is to be fired off asynchronously. When you trigger a listener, the content of that listener can't be guaranteed to finish executing before the thing that triggered it moves on. For that reason, they're not set up to offer a return.
Some more details about why you feel the desired logic must be from a Listener would be helpful, we may be able to guide you to a better pattern. For now though, I'd say you have a few options. In order of preference:
Abstract the content of your Listener's handle() method into a brand new service class. The class that you currently feel needs to access this Listener, and the Listener itself, can then reference this Service class independently of each other.
Don't fire your Listener class off an Event. Instead, instantiate it as a normal class, and then call it's handle() method directly to get your desired response.
Set config(['app.queue_driver' => 'sync']) to ensure listeners are fired off synchronously. (Make sure to change it back in a subsequent line after you trigger the listener that's intended to be synchronous, or else this could have unintended consequences for the rest of your App.) Then change your Listener so that whatever handle() does get stored in a property accessible by a new Getter method therein. This options is not advisable though. It can be done, but is so sloppy I hesitate to even suggest it. But I'm no stranger to making my code do weird things for the sake of unit-tests, so I'm not going to presume to know your circumstances.
If you need to get the result from a function, you shouldn't use the Event system to trigger that function.
You should refactor your code in one of two ways:
1) Stop using an event, and use something like a non-queued job/interaction.
$interaction = new Interaction;
$result = $interaction->handle($vars);
dd($result);
2) Pass all the variables that are required into the event, so that your listener can do any post-event processing within the listener itself.
I have React app which uses redux-observable with typescript. In this scenario, FetchAttribute Action gets triggered with a id and then make an ajax call.
In certain case, I would want to cancel the ajax request if "FETCH_ATTRIBUTE_CANCEL" action was triggered with the same id as of "FetchAttributeAction" action.
action$.ofType(FETCH_ATTRIBUTE)
.switchMap((request: FetchAttributeAction) => {
return ajax.getJSON(`/api/fetch-attribute?id=${request.id}`)
.flatMap((fetchUrl) => {
// return new action
})
.takeUntil(action$.ofType(FETCH_ATTRIBUTE_CANCEL));
});
interface FetchAttributeAction{
id: number;
}
Problem:
How do we cancel the execution based on action type + action data?
In my case, it would FETCH_ATTRIBUTE_CANCEL and id.
The key is to filter actions in the takeUntil notifier to only those which match the ID you care about.
action$.ofType(FETCH_ATTRIBUTE_CANCEL).filter(action => action.id === request.id)
So here's what it might look like:
Demo: https://stackblitz.com/edit/redux-observable-playground-xztkoo?file=fetchAttribute.js
const fetchAttributeEpic = action$ =>
action$.ofType(FETCH_ATTRIBUTE)
.mergeMap(request =>
ajax.getJSON(`/api/fetch-attribute?id=${request.id}`)
.map(response => fetchAttributeFulfilled(response))
.takeUntil(
action$.ofType(FETCH_ATTRIBUTE_CANCEL).filter(action => action.id === request.id)
)
);
You can also take a look at previous questions:
Redux Observable: If the same action is dispatched multiple times, how do I cancel one of them?
Independent chain cancellation in redux-observable?
Dispatch an action in response to cancellation
The OP also pointed out that they were using switchMap (as did I originally when I copied their code) which would have meant that the epic only ever had one getJSON at a time since switchMap will unsubscribe from previous inner Observables. So that also needed to be chained. Good catch!
I think you should be able to make takeUntil selective for a certain action id with pluck and filter.
ex:
.takeUntil(action%.ofType(FETCH_ATTRIBUTE_CANCEL)
.pluck('id')
.filter((cancelActionID) => cancelActionID === fetchID))
The non-obvious part to me is how to get the current fetchID to run that comparison. I might consider try using do to store in a temporary variable
I have a custom event that I want to fire using jQuery's trigger method:
$(wizard).trigger('validatingStepValues');
Then in the wizard's current step code, I subscribe to this event as follow:
$(wizard).bind('validatingStepValues', function (){
// Validating step's form data here; returning false on invalid state.
});
Then in my wizard, again I want to be able to stop user from going to the next step, if a false value is returned from validation process? I'd like to have something like:
$(wizard).trigger('validatingStepValues', validReturnCallback, invalidReturnCallback)
Have you considered using something like:
function wizardValidator(successCallback, failureCallback) {
return function() {
// Validating step's form data here;
if (wasValid && successCallback) {
successCallback();
}
else if (! wasValid && failureCallback) {
failureCallback();
}
return wasValid;
};
}
$(wizard).bind('validatingStepValues', wizardValidator(validReturnCallback, invalidReturnCallback));
This requires that you know the callbacks that you want to use at the time you bind the event listener. If you want to be able to use different callback functions at different times, you could define additional event types, like:
$(wizard).bind('validatingStep2Values', wizardValidator(validStep2ReturnCallback, invalidStep2ReturnCallback));
$(wizard).bind('validatingStep3Values', wizardValidator(validStep3ReturnCallback, invalidStep3ReturnCallback));
Alternately, events that you create by calling trigger() propagate up the DOM hierarchy. Returning false an event handler cancels this propagation. So you could bind your desired success callback function as an event listener on your wizard's parent node. That won't do anything to allow your failure callback to be executed, however.