Is there a way to have a hot observable from an EventEmitter (or equivalent available in Angular 2 alpha 46 / RxJS 5 alpha)? i.e. if we subscribe after the value is resolved, it triggers with the previously resolved value. Similar to what we have when always returning the same promise.
Ideally, only using Angular 2 objects (I read somewhere a light RxJS would be embedded later to remove the dependency), otherwise importing RxJS is fine. AsyncSubject seems to match my need, but it is not available in RxJS 5 alpha.
I tried the following, without success (never triggers). Any idea about how to use it?
let emitter = new EventEmitter<MyObj>();
setTimeout(() => {emitter.next(new MyObj());});
this.observable = emitter;
return this.observable.share();
Full plunker here comparing hot and cold
Usecase: reach some async objects only once (for example a series of HTTP calls merged/wrapped in a new EventEmitter), but provide the resolved async object to any service/component subscribing to it, even if they subscribe after it is resolved (the HTTP responses are received).
EDIT: the question is not about how to merge HTTP responses, but how to get a (hot?) observable from EventEmitter or any equivalent available with Angular 2 alpha 46 / RxJS 5 alpha that allows to subscribe after the async result is retrieved/resolved (HTTP is just an example of async origin). myEventEmitter.share() does not work (cf plunker above), although it works with the Observable returned by HTTP (cf plunker from #Eric Martinez). And as of Angular 2 alpha 46, .toRx() method does not exist any more, the EventEmitter is the observable and subject itself.
This is something working well with promises as long as we always return the same promise object. Since we have observers introduced with HTTP Angular 2 services, I would like to avoid mixing promises and observers (and observers are said to be more powerful than promises, so it should allow to do what is easy with promises).
Specs about share() (I haven't found doc for version 5 alpha - version used by Angular 2) - working on the Observable returned by the Angular 2 HTTP service, not working on EventEmitter.
EDIT: clarified why not using the Observable returned by HTTP and added that not using RxJS directly would be even better.
EDIT: changed description: the concern is about multiple subscriptions, not merging the HTTP results.
Thanks!
The functionality you seem to be describing is not that of a cold observable but more than of a Rx.BehaviourSubject. Have a look here for an explanation on Rxjs subjects : https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/subjects.md.
I quote from there :
BehaviourSubject is similar to ReplaySubject, except that it only stored the last value it published. BehaviourSubject also requires a default value upon initialization. This value is sent to observers when no other value has been received by the subject yet. This means that all subscribers will receive a value instantly on subscribe, unless the Subject has already completed.
The Rx.AsyncSubject would be the closest in behaviour to a promise :
AsyncSubject is similar to the Replay and Behavior subjects, however it will only store the last value, and only publish it when the sequence is completed. You can use the AsyncSubject type for situations when the source observable is hot and might complete before any observer can subscribe to it. In this case, AsyncSubject can still provide the last value and publish it to any future subscribers.
Two more comments:
in your plunker : this._coldObservable = emitter.share();. Using share returns a hot observable!
EventEmitter actually extends subject in the first place
UPDATE :
Wrapping an EventEmitter around an Rx.Observable:
function toRx ( eventEmitter ) {
return Rx.Observable.create(function ( observer ) {
eventEmitter.subscribe(function listener ( value ) {observer.onNext(value)});
// Ideally you also manage error and completion, if that makes sense with Angular2
return function () {
/* manage end of subscription here */
};
};
)
}
Once you have that Rx.Observable, you can apply share(), shareReplay(1), anything you want.
My bet is that the Angular team will sooner or later propose a brigding function but if you don't want to wait, you can do it yourself.
ReplaySubject is doing what I was looking for. #robwormald provided a working example on gitter I slightly modified to better demonstrate.
Exposing HTTP response:
import {Injectable} from 'angular2/angular2';
import {Http} from 'angular2/http';
import {ReplaySubject} from '#reactivex/rxjs/dist/cjs/Rx'
#Injectable()
export class PeopleService {
constructor(http:Http) {
this.people = new ReplaySubject(1);
http.get('api/people.json')
.map(res => res.json())
.subscribe(this.people);
}
}
Subscribing multiple times:
// ... annotations
export class App {
constructor(peopleService:PeopleService) {
people.subscribe(v => {
console.log(v)
});
//some time later
setTimeout(() => {
people.subscribe(v => {
console.log(v)
});
people.subscribe(v => {
console.log(v)
});
},2000)
}
}
Full plunker
EDIT: the BehaviorSubject is an alternative. In this usecase, the difference is the initial value, for example if we want to display content from cache before updating with the HTTP response.
Related
Calling foo two times creates two subscribers and that is not ok but even so the second call of foo seems to get stuck somewhere because console.log gets called only one time.
I know that I should not subscribe in this manner but I really do not see the solution to get myFunc to output the second result
const myFunc = functions.httpsCallable('myFunc')
function foo(inData){
myFunc({data:inData}).subscribe((res)=>{
console.log(res)
})
}
foo('aaaaa')
foo('bbbbb')
second attempt still no luck
myFunc(data){
return functions.httpsCallable('myFunc')(data)
}
async function foo(inData){
let res = await lastValueFrom(myFunc({data:inData}))
console.log(res)
}
foo('aaaaa')
foo('bbbbb')
The HttpsCallable function returns a promise by default. As you can see in this related question , there are a few differences between promises and observables, as it seems that you are already working with observables, I would try to convert the promise received from the function to an observable using this method, as suggested in this other question:
import { from } from 'rxjs';
const observable = from(promise);
Although, there are other methods suggested as well in the same question.
I would also like to point you to the Using observables to pass values, I think you might not have shared how you are creating the observer. I'm just guessing but you should also unsubscribe to clean up data ready for the next subscription. Also, missing next notification that is required.
As an extra, I would say that if you are not obligated to use observables, handle it as a promise.
Recently I've been moved to a new project which uses angular 6 as frontend framework and spring for REST services.
The project is in development for 2 years now and what I've observed was that almost all HTTP request made with angular HttpClient is then piped to take filter from rxjs. All REST APIs emit only one value. There's no need for manual cancellation and the laziness property of observables.
My intuition is that using toPromise() would be a better way to code.
What are your thoughts?
//customer-service.ts
constructor(private http: HttpClient) {
}
public getCustomers() {
return http.get('/customers');
}
//component.ts
public ngOnInit() {
this.customerService.getCustomers().pipe(take(1)).subscribe((customers) => {
//do some stuff
})
}
My proposed approach:
//customer-service.ts
constructor(private http: HttpClient) {
}
public getCustomers() : Promise<Array<Customer>> {
return http.get('/customers').toPromise();
}
//component.ts
public ngOnInit() {
this.customerService.getCustomers().then((customers: Array<Customer>) => {
//do some stuff
})
}
I think that my approach is better because it is strongly typed and it's cleaner.
Actually the accepted answer is misleading by saying that transforming an observable to a promise is like taking a step backward. Rather it's about what your needs are.
as stated in Angular Documentation:
Observables differentiate between chaining and subscription. Promises only have .then() clauses. This makes observables useful for creating complex transformation recipes to be used by other part of the system, without causing the work to be executed.
And the difference between toPromise() and take(1) (or for short use operator first()), is that take(1) completes after the 1st value emitted, while toPromise waits for the last emitted value (waits for the observable to complete then resolves the last value emitted).
Another difference between observables and promises that might interest you, is that Observable subscriptions are cancellable while promises are not. As a concrete example, say your API /customers takes time but you dont need the results anymore and you navigate to another page, unsubscribing cancels the HTTP request.
As you mentioned strongly typed we can do the same in observable approach.
public getCustomers():Observable<Array<Customer>> {
return http.get<Array<Customer>>('/customers');
}
//component.ts
public ngOnInit() {
this.customerService.getCustomers()
.pipe(take(1))
.subscribe((customers: Array<Customer>) => {
//do some stuff
});
}
For HttpRequest with single HttpResponse you may rely on promise
approach.
But HttpRequest with multiple response such as progress/stream of
data(blob) go for the Observable approach.
Try to use Observable as much as possible, once you familiar with you wont go for other options. So Give it a try!
I recommend to keep using the Observable way.
Why? Because later it will be easier to:
build upcoming logic (like mapping, filtering, etc.) for planned features
use the code, due mixing the two approach makes it harder to read/write
On the other hand you have right, your code is more readable as the code from others, but you can also change the Observable to be as good as yours (just add strong typing to it), and it still keeps the functionality and consistency of the Observables.
Going from observables to promises is a step back.
It's like going from a Porsche 911 to a Fiat multipla.
So no, you shouldn't use toPromise(), and no, your way isn't "better" (that's some ego there buddy !)
I think that my approach is better because it is strongly typed and it's cleaner.
Typing an HTTP answer isn't depending on pormises or observables, but on the developer himself. And cleaner is a matter of perspective, personally I hate seeing toPromise()s.
And the major drawback of your solution is that once converted to a promise, you can't pipe anything anymore, making your functions less versatile.
But their code isn't the best either. Usually this kind of behavior is used for stores and caches, are you sure you didn't omit something ?
Anyway, if not, and I only rely on the code provided, this would be the right code :
public getCustomers() {
return http.get<Customer[]>('/customers');
}
....
public ngOnInit() {
this.customerService.getCustomers()
.subscribe((customers) => {...})
}
Use case: Call a function every minute (60000 ms) that dispatches store action to fetch lastUpdated status of items, which upon response and filtering, updates the store, and updated store is read as an observable and displayed in the view). This needs to happen for as long as the web app is open (so indefinitely).
Currently, I'm using this:
this.refreshDate = window.setInterval(
() => this.store.dispatch(new FetchLastUpdate())
, 60000);
And when view is destroyed/dismounted, I delete the interval as so:
if (this.refreshDate) {
clearInterval(this.refreshDate);
}
Is this efficient/effective, or is it troublesome?
Why would I want to use an RxJS polling strategy like:
interval(60000)
.pipe(
startWith(0),
switchMap(() => this.store.dispatch(new FetchLastUpdate()))
);
Or
timer(0, 60000)
.pipe(
switchMap(() => this.store.dispatch(new FetchLastUpdate()))
);
TL;DR: window.setInterval() vs. RxJS timer()/interval()
Conclusion/answers (for ease of research):
There is great benefit to using RxJS functions to set an interval or perform polling, these benefits are explained in the selected answer but also in comments, but it is concluded (by discussions in the comments) that for the very simple requirement defined in the "Use case" section at the beginning of this post, it is unnecessary to use RxJS, and in fact if you are not using RxJS in any other part of your program, do not import it just for this, however in my case, I had already imported and used RxJS elsewhere.
Advantage of RxJS:
Laziness
You can create your Observables and until you call subscribe nothing is happening. Observable = pure function. This gives you more control, easier reasoning and allows for next point...
Composability
You can combine interval/timer with other operators creating custom logic very easily in unified way - for example you can map, repeat, retry, take... etc. see all operators
Error Handling
In case of an error you are responsible for calling clearTimeout/clearInterval - Observables are handling this for you. Resulting in cleaner code and fewer memory leak bugs.
Of course anything you do with Observables you can also do without Observables - but that's not the point. Observables are here to make your life easier.
Also note that interval/timer are not good observable factories for polling because they do not "wait" for your async action to finish (you can end up with multiple async calls running over each other). For that I tend to use defer and repeatWhen like this:
defer(() => doAsyncAction())
.pipe(
repeatWhen(notifications => notifications.pipe(delay(1234)))
);
window.setInterval doesn't care about your callbacks state, it'll execute at the given interval despite the status of the execution of the past callback, and the only way to make it stop and skip is clear the interval or reinitialize it.
On the other hand, RxJS Observable based solutions(interval, timer) allow you to pipe conditional operators (takeWhile, skipWhile for example) which allows you to add a stop or implement a stop-start logic by just flipping a boolean flag, instead of adding complicated logic of clearing the interval, and then recreating it.
And they are observables, you can listen to them all across the application, and attach any number of listeners to it.
Error Handling is better too, you subscribe to all successes, and handle everything in a catch callback.
I come from a synchronous programming background and I am having a hard time understanding observables.
Here is an extract of my service/provider (Ionic 2 project)
return this.http.get(`${this.serverApi}`)
.map(res => <Response[]>res.json());
and I will subscribe to that from the LoginPage. I have several questions regarding this.
Does the above code return an observable/ observer even if I didn't declare it as such?
The response is JSON. How do I do some checking/processing of the JSON and perform some action like if
res.auth_token==true
then do
localStorage.setItem(res.auth_token)
I believe it should be done in the provider class. Just a typical hint/example will be awesome.
Does the request actually happen when it hits the subscribe method?
Creating and returning Observable from Angular 2 Service mentions Subject and ReplaySubject. Should I use them instead?
The code will return an Observable
You can change the callback body to a block and add as much code as you want
return this.http.get(`${this.serverApi}`)
.map(res => {
let x = <Response[]>res.json();
// do something with x
res.auth_token == true;
return res; // with a block body an explicit`return` is required
});
Yes, calling .subscribe() will execute the http.get() and then all subsequent operators when the response arrives. Without .subscribe() nothing will happen.
Note: Some Angular APIs expect an Observable, in this case you must not call subscribe() because subscribe() returns a Subscription, not an Observable.
it depends what you try to accomplish. If you use http.get(), you already get an observable and there is no need for Subject, ReplaySubject, or any of the other possibilities.
Ok, this is a quick one, i'm kinda exhausted already and am confusing myself :D
I'm working with angular2 and RxJS Observables.
I have a service with a property "data", which is an Observable that get's set in the constructor, and a method to return this observable to subscribe to.
export class test{
private data: Observable<Object>
constructor(private http: Http){
this.data = this.http.get(url).map( res => res.json());
}
getData(): Observable<Object>{
return this.data
}
}
I have worked wit replaySubjects a while ago to always emit all values of the sequence to new subscribers. However, with the code above the Observable seems to emit it's latest value to new subscribers. Is this intended?
test(i: number) {
if (i > 0) {
setTimeout( () => {
this.dataService.getData().subscribe( (data) => {
this.debug.log(data);
this.test(i-1);
});
}, 2000);
}
}
test(4)
I get a value for every iteration. I am confused, 'cause a year ago when i wanted that behaviour, i got no new values when subscribing 'too late'.
Essentially, i just want to cache the result of the http.get, and deliver the same value to all subscribers, instead of making a new http request for every subscription (returning the http.get(url).. in getData())
I know this question is a bit old, but the answers seem to me quite confusing.
The Observable you return from the method getData() is just that, an Observable. So every time a consumer subscribes it gets the response. So it is working fine, but it is indeed making a new request every time.
In order to cache the result there are plenty of ways to do it depending on the behavior you want. To just cache a single request I would recommend t use the #publishBehavior operator:
export class test{
private data: Observable<Object>;
constructor(private http: Http){
this.data = this.http.get(url)
.map(res => res.json())
.publishBehavior([])
.refCount();
}
getData(): Observable<Object>{
return this.data;
}
}
The parameter passed to the publishBehavior is the initial value. With this two operators, the request will be made when the first subscriber arrived. Next subscribers will get the cached answer.
In others answers the use of Subjects has been suggested. The publishBehavior is using subjects under the hood. But to directly call next() it is consider bad practice unless there is no other remedy, and thats not the case for this scenario in my opinion. Even if you use Subjects directly, it will be wise to return an Observable to the Components by using the #asObservable() operator so the component won't have access to the next, error and complete methods.
No. You need to use Subject for this. It has a method next() to which you will send your newly arrived property so that it pushes it to the subscribers.
In addition to this, you should create a service that will be a singleton. Whenever your components instantiate it in a constructor, they will receive the object already formed with all the data. There will be no need to fetch the data every time.
Also, instead of instantiating your data in the constructor, implement OnInit and do the calls to the server from there.