How to dispose nested Rx web request calls in Windows Phone 7 - windows-phone-7

In my application i am using chain of of web request call for fetching data from the net. Ie from the result of one request i will send other request and so on. But when i am disposing the web request, only the parent request is disposing. The two other request are still running. How i can cancel all these request in Rx

For your subscription to terminate everything, you either cannot break the monad or you need to make sure that you work into the IDisposable model.
To keep the monad (ie. stick with IObservables):
var subscription = initialRequest.GetObservableResponse()
.SelectMany(initialResponse =>
{
// Feel free to use ForkJoin or Zip (intead of Merge) to
// end up with a single value
return secondRequest.GetObservableResponse()
.Merge(thirdRequest.GetObservableResponse());
})
.Subscribe(subsequentResponses => { });
To make use of the IDisposable model:
var subscription = initialRequest.GetObservableResponse()
.SelectMany(initialResponse =>
{
return Observable.CreateWithDisposable(observer =>
{
var secondSubscription = new SerialDisposable();
var thirdSubscription = new SerialDisposable();
secondSubscription.Disposable = secondRequest.GetObservableResponse()
.Subscribe(secondResponse =>
{
// Be careful of race conditions here!
observer.OnNext(value);
observer.OnComplete();
});
thirdSubscription.Disposable = thirdRequest.GetObservableResponse()
.Subscribe(thirdResponse =>
{
// Be careful of race conditions here!
});
return new CompositeDisposable(secondSubscription, thirdSubscription);
});
})
.Subscribe(subsequentResponses => { });

One approah is by using TakeUntil extnsion method as described here. In your case, the event that takes this method as parameter could be some event thrown by the parent request.
If you could show us some code we can face the problem more specifically.
regards,

Related

rxjs operator to define logic after subscribe call

const source = Rx.Observable.of(1);
const example = source
.do(val => console.log('do called'));
example.subscribe(val => console.log('subscribe called'));
//Output :
do called
subscribe called
This exemple shows that do is executed before subscribe.
Which operator do I need to use to define logic after subscribe is executed ?
I need this to define logic one time and that must be executed after each subscribe call that helps also to respect SRP (Single responsibility Principle) an example is to handle caching logic in interceptor using some kind of specific operator that I am looking for and subscribe in services
The way I handle an Interceptor is as follows, it may help if I understand your requirements correctly.
...
private interceptor(observable: Observable<Response>): Observable<Response> {
return observable
.map(res => {
return res;
})
.catch((err) => {
//handle Specific Error
return Observable.throw(err);
})
.finally(() => {
//After the request;
console.info("After the Request")
});
}
protected get(req: getHttpParams): Observable<Response> {
return this.interceptor(this.httpClient.get(`${path}/${String(req.id)}`, req.options));
}
...
I would also recommend taking a look at Angular 5's in-built interceptor for http requests specifically

Angular 2/4 & RxJS - Subscriptions within forEach - Ensuring flow control doesn't continue until all Observables are complete

The goal is to iterate through a collection of IDs, making an HTTP call for each ID. For each ID, I'm using a service with a get() method that returns an Observable. Each time the get() method is called, I'm subscribing to the returning Observable and trying to push the result into an array, which will eventually get passed on to a different method for a new operation.
Relevant service method:
public get(departmentId: number): Observable<IDepartmentModel> {
return super.get<IDepartmentModel>(departmentId);
}
note: the super class is leveraging Angular Http, which is well tested and confirmed to be working correctly. The problem with the logic isn't here...
Relevant component methods:
note the departmentService.get() call that's being called several times within the forEach.
setInitialDepartmentsAssignedGridData(): void {
this.settingsForDropdownSelectedCompanyId = this.userForm.get('defaultCompany').get('defaultCompanyId').value;
let departments: IDepartmentModel[] = [];
this.userService.user.getValue() //confirmed: valid user is being pulled back from the userService (logic is fine here..)
.userCompanies.find(comp => comp.companyId === this.settingsForDropdownSelectedCompanyId) // getting a valid match here (logic is fine here..)
.departmentIds.forEach(deptId => this.departmentService.get(deptId).first().subscribe(dept => { // getting a valid department back here (logic is fine here...)
departments.push(dept); // HERE LIES THE PROBLEM
}));
this.setDepartmentsAssignedRowData(departments);
}
setDepartmentsAssignedRowData(departments: IDepartmentModel[]): void {
console.log('setDeptAssignedRowData called'); // confirmed: method is getting called...
console.log(departments); // confirmed: fully-composed collection of departments logged to the console...
departments.forEach(dept => {
console.log(dept);
}); // Y U NO WORK!?
departments.map((department) => {
console.log(department); // Y U NO WORK?
this.departmentAssignedRowData.push({
departmentId: department.id,
departmentName: department.name
});
});
this.departmentAssignedGridOptions.api.setRowData(this.departmentAssignedRowData);
}
The problem is, although what's getting logged to the console is a fully-composed department-objects array, it's not TRULY "there"; what's getting passed to setDepartmentsAssignedRowData is an empty array.
I'm sure what's happening is that the async operations are not complete before the departments array gets passed to the second method. Some of what I've read online says to use forkJoin, but I can't see how that will look in this context. I've also read concatMap may work, but again, in this context, I'm not sure how to make that work...
In this context, how do I leverage RxJS to make sure the intended, fully-composed departments array is truly ready to be passed?
thanks for any insight you can provide. help is much appreciated!
You are correct, you need forkJoin
let observableArray = this.userService.user.getValue()
.userCompanies.find(comp => comp.companyId === this.settingsForDropdownSelectedCompanyId)
.departmentIds.map(deptId => this.departmentService.get(deptId)) // map is building out an array of observables
This will be an array of http request observables that you want to make in parallel. Now you can pass this array to forkJoin.
Observable.forkJoin(...observableArray)
The return of forkJoin will be an array of results from observableArray. forkJoin will not emit to the next operator in the sequence until all of the observables in observableArray have completed (so when all of the http requests have finished)
So altogether the code will be
let observableArray = this.userService.user.getValue()
.userCompanies.find(comp => comp.companyId === this.settingsForDropdownSelectedCompanyId)
.departmentIds.map(deptId => this.departmentService.get(deptId));
Observable.forkJoin(...observableArray).subscribe(res => {
// res = [resId0, resId1, resId2, ..., resIdX];
});
You mentioned passing the result to another operator. If that operator is another http request where you pass an array of data (from forkJoin), then you can use the flatMap operator.
Observable.forkJoin(...observableArray)
.flatMap(res => {
return this.otherApi(res);
})
.subscribe(res => {
// res is the result of the otherApi call
});
flatMap will chain your api requests together. So altogether what is happening is
run array of observables in parallel
once complete, run second api (otherApi)

How to handle progress update using ReactiveX Observables/Subjects?

I'm writing an Angular app which uses the ReactiveX API to handle asynchronous operations. I used the API before in an Android project and I really like how it simplifies concurrent task handling. But there is one thing which I'm not sure how to solve in a right way.
How to update observer from an ongoing task? The task in this case will take time to load/create a complex/large object and I'm able to return intermediate progress, but not the object itself. The observable can only return one dataType. Therefor I know two possibilities.
Create an object which has a progress field and a data field. This object can be simply returned with Observable.onNext(object). The progress field will update on every onNext, while the data field is empty until the last onNext, which will set it to the loaded value.
Create two observables, a data observable and a progress observable. The observer hast to subscribe to the progress observable for progress updates and to the data observable to be notified when the data is finally loaded/created. These can also be optionally be zipped together for one subscription.
I used both techniques, they both work, but I want to know if there is a unified standard, a clean way, how to solve this task. It can, of course, as well be a completly new one. Im open for every solution.
After careful consideration I use a
solution similar to option two in my question.
The main observable is concerned with the actual result of
the operation.
A http request in this case, but the File iteration example is similar.
It is returned by the "work" function.
A second Observer/Subscriber can be added through a function parameter. This subscriber is concerned only with
the progress information. This way all operations are nullsafe and no type checks are needed.
A second version of the work function, without the progress Observer,
can be used if no progress UI update is needed.
export class FileUploadService {
doWork(formData: FormData, url: string): Subject<Response> {
return this.privateDoWork(formData, url, null);
}
doWorkWithProgress(formData: FormData, url: string, progressObserver: Observer<number>): Subject<Response> {
return this.privateDoWork(formData, url, progressObserver);
}
private privateDoWork(formData: FormData, url: string, progressObserver: Observer<number> | null): Subject<Response> {
return Observable.create(resultObserver => {
let xhr: XMLHttpRequest = new XMLHttpRequest();
xhr.open("POST", url);
xhr.onload = (evt) => {
if (progressObserver) {
progressObserver.next(1);
progressObserver.complete();
}
resultObserver.next((<any>evt.target).response);
resultObserver.complete()
};
xhr.upload.onprogress = (evt) => {
if (progressObserver) {
progressObserver.next(evt.loaded / evt.total);
}
};
xhr.onabort = (evt) => resultObserver.error("Upload aborted by user");
xhr.onerror = (evt) => resultObserver.error("Error");
xhr.send(formData);
});
}
Here is a call of the function including the progress Subscriber. With this solution the caller of the upload function must
create/handle/teardown the progress subscriber.
this.fileUploadService.doWorkWithProgress(this.chosenSerie.formData, url, new Subscriber((progress) => console.log(progress * 100)).subscribe(
(result) => console.log(result),
(error) => console.log(error),
() => console.log("request Completed")
);
Overall I prefered this solution to a "Pair" Object with a single subscription. There is no null handling nececcary, and
I got a clean seperation of concerns.
The example is written in Typescript, but similar solutions should be possible with other ReactiveX implementations.

Time-based cache for REST client using RxJs 5 in Angular2

I'm new to ReactiveX/RxJs and I'm wondering if my use-case is feasible smoothly with RxJs, preferably with a combination of built-in operators. Here's what I want to achieve:
I have an Angular2 application that communicates with a REST API. Different parts of the application need to access the same information at different times. To avoid hammering the servers by firing the same request over and over, I'd like to add client-side caching. The caching should happen in a service layer, where the network calls are actually made. This service layer then just hands out Observables. The caching must be transparent to the rest of the application: it should only be aware of Observables, not the caching.
So initially, a particular piece of information from the REST API should be retrieved only once per, let's say, 60 seconds, even if there's a dozen components requesting this information from the service within those 60 seconds. Each subscriber must be given the (single) last value from the Observable upon subscription.
Currently, I managed to achieve exactly that with an approach like this:
public getInformation(): Observable<Information> {
if (!this.information) {
this.information = this.restService.get('/information/')
.cache(1, 60000);
}
return this.information;
}
In this example, restService.get(...) performs the actual network call and returns an Observable, much like Angular's http Service.
The problem with this approach is refreshing the cache: While it makes sure the network call is executed exactly once, and that the cached value will no longer be pushed to new subscribers after 60 seconds, it doesn't re-execute the initial request after the cache expires. So subscriptions that occur after the 60sec cache will not be given any value from the Observable.
Would it be possible to re-execute the initial request if a new subscription happens after the cache timed out, and to re-cache the new value for 60sec again?
As a bonus: it would be even cooler if existing subscriptions (e.g. those who initiated the first network call) would get the refreshed value whose fetching had been initiated by the newer subscription, so that once the information is refreshed, it is immediately passed through the whole Observable-aware application.
I figured out a solution to achieve exactly what I was looking for. It might go against ReactiveX nomenclature and best practices, but technically, it does exactly what I want it to. That being said, if someone still finds a way to achieve the same with just built-in operators, I'll be happy to accept a better answer.
So basically since I need a way to re-trigger the network call upon subscription (no polling, no timer), I looked at how the ReplaySubject is implemented and even used it as my base class. I then created a callback-based class RefreshingReplaySubject (naming improvements welcome!). Here it is:
export class RefreshingReplaySubject<T> extends ReplaySubject<T> {
private providerCallback: () => Observable<T>;
private lastProviderTrigger: number;
private windowTime;
constructor(providerCallback: () => Observable<T>, windowTime?: number) {
// Cache exactly 1 item forever in the ReplaySubject
super(1);
this.windowTime = windowTime || 60000;
this.lastProviderTrigger = 0;
this.providerCallback = providerCallback;
}
protected _subscribe(subscriber: Subscriber<T>): Subscription {
// Hook into the subscribe method to trigger refreshing
this._triggerProviderIfRequired();
return super._subscribe(subscriber);
}
protected _triggerProviderIfRequired() {
let now = this._getNow();
if ((now - this.lastProviderTrigger) > this.windowTime) {
// Data considered stale, provider triggering required...
this.lastProviderTrigger = now;
this.providerCallback().first().subscribe((t: T) => this.next(t));
}
}
}
And here is the resulting usage:
public getInformation(): Observable<Information> {
if (!this.information) {
this.information = new RefreshingReplaySubject(
() => this.restService.get('/information/'),
60000
);
}
return this.information;
}
To implement this, you will need to create your own observable with custom logic on subscribtion:
function createTimedCache(doRequest, expireTime) {
let lastCallTime = 0;
let lastResult = null;
const result$ = new Rx.Subject();
return Rx.Observable.create(observer => {
const time = Date.now();
if (time - lastCallTime < expireTime) {
return (lastResult
// when result already received
? result$.startWith(lastResult)
// still waiting for result
: result$
).subscribe(observer);
}
const disposable = result$.subscribe(observer);
lastCallTime = time;
lastResult = null;
doRequest()
.do(result => {
lastResult = result;
})
.subscribe(v => result$.next(v), e => result$.error(e));
return disposable;
});
}
and resulting usage would be following:
this.information = createTimedCache(
() => this.restService.get('/information/'),
60000
);
usage example: https://jsbin.com/hutikesoqa/edit?js,console

Angular2: Example with multiple http calls (typeahead) with observables

So I am working on couple of cases in my app where I need the following to happen
When event triggered, do the following
List item
check if the data with that context is already cached, serve cached
if no cache, debounce 500ms
check if other http calls are running (for the same context) and kill them
make http call
On success cache and update/replace model data
Pretty much standard when it comes to typeahead functionality
I would like to use observables with this... in the way, I can cancel them if previous calls are running
any good tutorials on that? I was looking around, couldn't find anything remotely up to date
OK, to give you some clue what I did now:
onChartSelection(chart: any){
let date1:any, date2:any;
try{
date1 = Math.round(chart.xAxis[0].min);
date2 = Math.round(chart.xAxis[0].max);
let data = this.tableService.getCachedChartData(this.currentTable, date1, date2);
if(data){
this.table.data = data;
}else{
if(this.chartTableRes){
this.chartTableRes.unsubscribe();
}
this.chartTableRes = this.tableService.getChartTable(this.currentTable, date1, date2)
.subscribe(
data => {
console.log(data);
this.table.data = data;
this.chartTableRes = null;
},
error => {
console.log(error);
}
);
}
}catch(e){
throw e;
}
}
Missing debounce here
-- I ended up implementing lodash's debounce
import {debounce} from 'lodash';
...
onChartSelectionDebaunced: Function;
constructor(...){
...
this.onChartSelectionDebaunced = debounce(this.onChartSelection, 200);
}
For debaunce you can use Underscore.js. The function will look this way:
onChartSelection: Function = _.debounce((chart: any) => {
...
});
Regarding the cancelation of Observable, it is better to use Observable method share. In your case you should change the method getChartTable in your tableService by adding .share() to your Observable that you return.
This way there will be only one call done to the server even if you subscribe to it multiple times (without this every new subscription will invoke new call).
Take a look at: What is the correct way to share the result of an Angular 2 Http network call in RxJs 5?

Resources