I have an $http promise in an angular app like this:
this.data = $http.get('/api/foo', {})
Other parts of my app then add success and error handlers to this promise.
My problem is that I want to refresh the information within the this.data variable and then re-run all the promise's attached handlers. Can this be done with some sort of this.data.$refresh() method, or would I have to store all the handlers somewhere else and reattach them to a new $http.get?
EDIT: Maybe a slightly clearer example:
this.data = $http.get('/api/foo', {})
this.data.success(doSomething)
// doSomething() runs because the response arrives.
this.data.someMagic()
// doSomething() runs again without being reattached.
What I want to avoid is this:
this.data = $http.get('/api/foo', {})
this.data.success(doSomething)
// Time passes...
this.data = $http.get('/api/foo', {}) // All old handlers have now been thrown away.
this.data.success(doSomething)
This is because there are several handlers on both success and error, and they are added by different controllers and services, so it would require some messy callback system to get them all to reattach their handlers every time the variable was updated.
re-run all the promise's attached handlers.
No, that's impossible. By contract, a promise resolves and executes its handlers only once.
would I have to store all the handlers somewhere else and reattach them to a new $http.get?
Yes, that's a possible solution, although it looses all the nice properties of promises like chainability. You might as well simply put an EventEmitter and implement some kind of pub-sub (see Angularjs pubsub vs $broadcast for example).
If you want to have a real stream interface with all kinds of goodies, you may want to look into FRP, e.g. with Bacon.js and angular-bacon.
Related
I have the following subscription in an angular application:
private _sub: Subscription;
On initialization I subscribe to a firebase get function:
this._sub = this.service.get('database1').subscribe(
data => {
this.ListOfData = data;
}
);
But depending on user input I change database1 to other collections, or use a different method instead of get().
When I override _sub will the previous stream get automatically unsubcribed, or do I have to manually do it before overriding?
Short answer: No it doesn't...I'll share the details on why
Whenever .subscribe() is called, a new instance of a Subscription is created that holds resources that listen to the execution of the Observable. If you call .subscribe() and do not call .unsubscribe(), your Subscription is still utilizing resources. In some cases (depending on the logic associated to the Observable/Subject) it can result in unexpected behavior in your application and it can impact performance. This is why the best practice is to call .unsubscribe() on your Subscription when you are done with it.
There are also functions called RxJS operators that can help you to manage your subscriptions by automatically unsubscribing based on certain criteria.
No, you have to unsubscribe. Sometimes they stay like sleeper cells to damage your application performance. Best practice is unsubscribe.
I'm learning RxJS and am rather confused as to where the "listeners" are (in the Observable or the Observer), how they are subscribed/unsubscribed, and what happens when an Observer is "no longer interested in" an Observable, such as when you use take or takeUntil.
For the first part -- what's subscribed to what, what's a listener -- I'm confused by the seeming contradiction between these statements. From http://reactivex.io/rxjs/manual/overview.html we read that Observers are not 'listeners' to Observables
This is drastically different to event handler APIs like
addEventListener / removeEventListener. With observable.subscribe, the
given Observer is not registered as a listener in the Observable. The
Observable does not even maintain a list of attached Observers.
but in http://reactivex.io/learnrx/ it says (Exercise 30) (highlighting mine) that
An Observable based on an Event will never complete on its own. The
take() function creates a new sequence that completes after a discrete
number of items arrive. This is important, because unlike an Event,
when an Observable sequence completes it unsubscribes all of its
listeners. That means that if we use take() to complete our Event
sequence, we don't need to unsubscribe!
This seems contradictory to me. When you set up an Observable with, for example, fromEvent, where is the event listener? When you use take(1), for instance, on an Observable based on DOM events, what happens after the first event is sent to the observer? Does the Observer unsubscribe from the Observable, which continues to emit events, it's just that the Observer isn't listening to them anymore? Or does the Observable somehow unsubscribe the Observer, that is, the eventListener was in the Observable, not the Observer?
Thanks for any clues -- obviously I'm not seeing the forest for the trees, but the tutorials I'm working through, while they are good at trying to explain it conceptually, leave me confused as to what's actually going on.
The first part is being rather particular about its use of words in order to highlight that subscribing to an observable is a matter of calling a function (or more likely a chain of functions) to run all the code they contain. The second part is less particular about its wording, but it's not really talking about the same thing. If you like, the second part would be better worded as "when an observable completes, it calls teardown logic on its observers.
Let me try to describe what i mean when i say that subscribing to an observable is a matter of calling a chain of functions. Consider the following super simple example:
For a super simple example, suppose i create this observable:
const justOne = Rx.Observable.create(function realSubscribe(observer) {
observer.next(1);
observer.complete();
});
justOne.subscribe(val => console.log(val));
If i then call justOne.subscribe(val => console.log(val)), doing so will immediately call the function i named realSubscribe. It then does observer.next(1), which results in logging out val, then it does observer.complete(). And that's it.
No where in this process did the observable create or augment a list of subscribers; it just ran through the code sequentially and then was done.
Now moving onto a slightly more realistic example, let's consider fromEvent. If i were to implement it, it might look something like this (the real implementation is more complicated, but this gets the gist of it):
function fromEvent(element, eventName) {
return Rx.Observable.create(function subscribeToEvent(observer) {
element.addEventListener(eventName, observer.next);
return function cleanup() {
element.removeEventListener(eventName, observer.next);
}
});
}
const observable = fromEvent(document, 'click');
const subscription = observable.subscribe(event => console.log(event));
Now when i call observable.subscribe, it runs subscribeToEvent, and in so doing it calls addEventListener on the document. document.addEventListener does result in the document keeping a list of event listeners, but that's because of the way addEventListener is implemented, not something common to all observables. The observable itself doesn't keep track of any listeners. It just calls what it's told to call, and then returns a cleanup function.
Next up let's look at take. As before the real implementation is more complicated, but here's roughly what it does:
// In the real `take`, you don't need to pass in another observable since that's
// available automatically from the context you called it in. But my sample code
// has to get it somehow.
function take(count, otherObservable) {
return new Observable(function subscribeToTake(observer) {
let soFar = 0;
otherObservable.subscribe((value) => {
observer.next(value);
soFar++;
if (soFar >= count) {
observer.complete();
}
});
});
}
const clickObservable = fromEvent(document, 'click');
take(1, clickObservable).subscribe(event => console.log(event))
As mentioned in the comment, the syntax i'm using doesn't quite match how it would be use in rxjs, but that's because to mimic that would require a more full implementation. Anyway, the main thing to draw your attention to is that we're starting to produce a chain of functions:
When i call .subscribe, that calls subscribeToTake. This sets up a counter, and then calls otherObservable.subscribe, which is subscribeToEvent. subscribeToEvent then calls document.addEventListener.
Take's job is to sit in the middle of this function chain. It keeps track of how many values have been emitted so far. If the count is low enough, it just forwards the values along. But once the count is reached, it will call complete, thus ending the observable. Calling complete causes the observable to run any teardown logic it has, or anything its chain has. There's no teardown logic for take, but fromEvent will run some teardown logic to remove the event listener.
This problem should be clear: I want to write into a log file using a statement like the following. You can assume that this statement is inside a click handler for a button. There are two questions embedded in this pseudocode.
pLogInfo("local info").then(pauseUntilSettled).catch(err); // This is my goal
Here are my library functions (each returns a Promise):
// Get info asynchronously from a server (simple, should work)
function pGetServerInfo()
{
// then value: "server info"
} // pGetServerInfo
// Write into a file asynchronously (simple, should work)
function pWriteFile(path,string)
{
// no then value
} // pWriteFile
// Write formatted info into a log file asynchronously
function pLogInfo(localInfo)
{
pGetServerInfo().then(p2);
} // pLogInfo
function p2(serverInfo)
{
// Should write "local info, server info"
// But where is the local info? It got lost.
return pWriteFile('log',localInfo+','+serverInfo);
} // p2
Usage:
pLogInfo("local info").then(pauseUntilSettled).catch(err);
function pauseUntilSettled()
{
// How to wait before returning from
// the button click event handler?
} // pauseUntilSettled
ADDED 8/27/19:
Several possible solutions to this common problem occur to me:
Attach an object to one of the top functions in your chain (p.data={}). You can store any arguments or callbacks you wish in the object and reference them in any asynchronous 'then' code. This works because the function that is the parent of the object has global scope. It can fail if you fire another top-level Promise of the same thread while an existing Promise is not yet settled, because the new execution thread will share and overwrite the object. I have successfully used this approach, a variant of the above global formulation, but it is clearly unsafe.
Create a closure function to propagate the asynchronous thread. A closure function contains a snapshot of its arguments and referenced global variables. I have not yet gotten this idea to work, but it seems reasonable.
Create a new Promise, either as part of the thread of Promises or as a separate helper, that calls its resolve function with an object instead of a single value. Use that object to propagate more than one value to each "then" function. I have not gotten this idea to work either.
I hope these ideas inspires someone (including myself) to come up with a solution, because it is a very common problem that is not often discussed.
Satpal gave a great answer for promises. Another option is to use the RXJS library and utilize observables which are built on top of Promises. They have a next(), error() and complete() block where code will only execute once values are received in the stream. If you want to wait on multiple services to respond you can combine the streams together in multiple ways.
I know this is not the exact answer you are looking for but it is a very useful library. Here is the documentation. https://rxjs-dev.firebaseapp.com/api/index/class/Observable
Solution:
You can put intermediate values in scope in any later 'then' function explicitly, by using 'bind'. It is a nice solution that doesn't require changing how Promises work, and only requires a line or two of code to propagate the values just like errors are already propagated.
Here is a complete example:
// Get info asynchronously from a server
function pGetServerInfo()
{
// then value: "server info"
} // pGetServerInfo
// Write into a file asynchronously
function pWriteFile(path,string)
{
// no then value
} // pWriteFile
// The heart of the solution: Write formatted info into a log file asynchronously,
// using the pGetServerInfo and pWriteFile operations
function pLogInfo(localInfo)
{
var scope={localInfo:localInfo}; // Create an explicit scope object
var thenFunc=p2.bind(scope); // Create a temporary function with this scope
return (pGetServerInfo().then(thenFunc)); // Do the next 'then' in the chain
} // pLogInfo
// Scope of this 'then' function is {localInfo:localInfo}
function p2(serverInfo)
{
// Do the final 'then' in the chain: Writes "local info, server info"
return pWriteFile('log',this.localInfo+','+serverInfo);
} // p2
This solution can be invoked as follows:
pLogInfo("local info").then().catch(err);
(Note: a more complex and complete version of this solution has been tested, but not this example version, so it could have a bug as written.)
I'm trying to understand how Observables and RxJS works, so this might be not at all the point of how to use them.
I have an Angular2 application and am additionally using RxJS Observables to send events around. Now for a special type of error events, I'd like to know if the event has already been handled by another Subscriber. Multiple Subscribers might exist on the Observable and some might take full responsibility of the event so that others won't get it anymore.
The idea comes from how Routed Events work in WPF. In the event handler you get the RoutedEventArgs parameter, which has a Property Handled:
If setting, set to true if the event is to be marked handled; otherwise false. If reading this value, true indicates that either a class handler, or some instance handler along the route, has already marked this event handled. false.indicates that no such handler has marked the event handled.
Another implementation example would be how the middleware works in the ASP.NET Core Pipeline - https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware - You can either call the next middleware or just return a result.
I was thinking about adding the Handled property to the event I'd throw into the observable pipe, but I'm not sure if it's the idiomatic way of doing this in RxJS.
Typically the way you do this with observables is you don't hand the observable to everyone and everyone subscribes to it. Instead you give each interested party a chance to "add to the pipeline" and then finally subscribe once. There are many ways to do this. The easiest is to not actually give anyone the observable. But instead let them provide you with callbacks:
class Foo {
observable = ...;
callbacks = [];
addCallback(callback) { this.callbacks.push(callback); }
constructor() {
// subscribe to the observable and call any registered callbacks
this.observable.subscribe(e => {
for (const cb of this.callbacks) {
// callback returns true if it has "handled" the event
if (cb(e)) {
return; // do not call any other callbacks
}
}
});
}
}
const foo = new Foo();
// subscriber1 handles "type1" events
foo.addCallback(ev => ev.type === "type1");
// subscriber2
foo.addCallback(ev => ev.type === "type2");
This is the simplest way. There are other ways where Foo exposes observables for each client and monitors their results to build the pipeline.
I'm trying to write a function that performs an asynchronous task and returns a promise while ensuring cleanup occurs after any callbacks are fulfilled. However to do this, it seems I need to know the callback in advance so I can ensure that it happens before the cleanup happens.
Currently, the general structure of the function looks like this:
function doSomethingWithResourceAsync(someParameter, usePreparedResourceCb) {
var resource = acquireResource(someParameter);
return prepareResourceAsync(resource)
.then(usePreparedResourceCb)
.finally(doCleanup);
function doCleanup() {
releaseResource(resource);
}
}
To call it, I would do this:
doSomethingWithResourceAsync(myParameter, myCallback)
.then(andSoOn);
function myCallback(proxyObj) {
return doMagicAsync(proxyObj);
}
This is the only way I can get it to work.
However, I want to write it in a way that I can chain my callback instead while not having to pass around a cleanup callback. So I'd like to call it like this:
function doSomethingWithResourceHopefullyAsync(myParameter) {
var resource = acquireResource(someParameter);
return prepareResourceAsync(resource)
.finally(doCleanup); // uh oh
function doCleanup() {
releaseResource(resource);
}
}
doSomethingWithResourceHopefullyAsync(myParameter)
.then(myCallback) // too bad, already cleaned up
.then(andSoOn);
This doesn't work however because the cleanup happens before myCallback gets control and messes things up.
If possible, how can I structure my method to accomplish my goal? Or is what I have the best I can do for this situation?
I have a feeling I could use deferreds to accomplish my goal but I don't know how to set that up to make this work.
The API I'm trying to develop is to be consumed by users who won't necessarily know the intricacies of asynchronous methods so I want to hide that as much as possible.
What you have is the disposer pattern. Props for figuring it out on your own :)
"Passing" the callback in is necessary because it creates a scope which is what effectively enables the cleanup. You'd know how the callback is "done" by it returning a promise. The fact you need a scope is fundamental, because it's what well... scopes the cleanup. Binding resource allocation to instantiation via scope (RAII) is a useful technique for what you're doing.
I would do something like:
function withResource(handler){
return acquireResource(). // important to return to chain, like your code
then(handler).finally(cleanup);
}
Which is effectively what you already have.
As the comments suggest, bluebird's using is a very useful abstraction, it returns disposers which give you a lot of power for cleanup and clean up a lot of type errors. I highly recommend it (though I'm obviously biased).