Reputation: 35
I want to execute a function when a takeUntil operator is triggered (outside ajaxObservable). In this way is fired immediately, because I call observer.complete(), but if I don't call it there will be memory leaks right?
What's a good way to achieve this?
export const ajaxObservable = (url, method, data, params) => {
let cancelToken = axios.CancelToke.source()
return Observable
.fromPromise(axios({/* axios params */}))
.map(r => r.data)
.catch(err => Observable.throw(err))
.merge(
new Observable(observer => {
observer.complete()
return () => cancelToke.cancel()
})
)
}
Upvotes: 1
Views: 649
Reputation: 15401
One issue with your approach is that calling ajaxObservable()
doesn't return a lazy Observable. Instead, the ajax require is immediately made by axios, even if no one subscribes to the returned Observable.
While there are exceptions, typically it's best practice to have custom Observables like this be lazy, so the same expectations hold for users.
To do that, you'll want to return a new anonymous Observable, very similar to how you might do this with Promises. Inside our custom Observable subscribe handler, there isn't much need to using fromPromise
or any rxjs since we just need to call axios and then
.
As a bonus, when we do it this way the solution to your original question becomes more apparent: if someone unsubscribes, we can call the cancelToken.cancel()
.
export const ajaxObservable = (url, method, data, params) => {
return new Observable(observer => {
let cancelTokenSource = axios.CancelToken.source();
let options = {
url,
method,
data,
params,
withCredentials: true,
cancelToken: cancelTokenSource.token
};
axios(options)
.then(response => {
observer.next(response.data);
observer.complete(); // commonly forgotten, but critical!
}, error => {
observer.error(error);
});
return () => cancelTokenSource.cancel();
});
};
btw .catch(err => Observable.throw(err))
is effectively a noop, rethrowing the same error again.
You might be interested to know that rxjs comes with an AjaxObservable, which makes something like axios unnecessary. The documentation for it unfortunately doesn't show up correctly in the rxjs v5 docs, but it can be found inline: http://reactivex.io/rxjs/file/es6/observable/dom/AjaxObservable.js.html It has a pretty standard API that's similar to most ajax utilities.
/**
* Creates an observable for an Ajax request with either a request object with
* url, headers, etc or a string for a URL.
*
* @example
* source = Rx.Observable.ajax('/products');
* source = Rx.Observable.ajax({ url: 'products', method: 'GET' });
*
* @param {string|Object} request Can be one of the following:
* A string of the URL to make the Ajax call.
* An object with the following properties
* - url: URL of the request
* - body: The body of the request
* - method: Method of the request, such as GET, POST, PUT, PATCH, DELETE
* - async: Whether the request is async
* - headers: Optional headers
* - crossDomain: true if a cross domain request, else false
* - createXHR: a function to override if you need to use an alternate
* XMLHttpRequest implementation.
* - resultSelector: a function to use to alter the output value type of
* the Observable. Gets {@link AjaxResponse} as an argument.
*/
It also has shorthands like ajax.getJSON
etc.
Upvotes: 2