DynamicData Exceptions within Transform don't bubble up - xamarin.forms

Given the following snippet
var connection = Session.MarketRecords.Connect()
.Group(r => r.SettledDate.Date)
.Transform(grouping => new DayReport(grouping))
.Bind(DayReports, Updater)
.Subscribe();
Looks like exceptions that are thrown within the Transform function are being swallowed and thus making it hard to figure out when things go wrong. I was only able to identify this because the DayReports "list" was not being populated (and I knew there were records) so I thought it could be the Binding that was wrong but after putting breakpoints "everywhere" I figured the constructor of DayReport had a bug which would cause an exception to be thrown under certain circumstances. Is there any recommended way to capture exceptions that occur under these circumstances?

This has been replied on the Reactive slack, so I'm posting it here for reference.
Subscribe has an overload that takes two arguments, the second one being an exception handler and that would be the appropriate way of handling exceptions thrown during any of the previous calls. So the following worked fine:
var connection = Session.MarketRecords.Connect()
.Group(r => r.SettledDate.Date)
.Transform(grouping => new DayReport(grouping))
.Bind(DayReports, Updater)
.Subscribe((change) =>
{
Console.WriteLine(change.Count);
},
(ex) =>
{
Console.WriteLine(ex.Message);
});

Related

How do I throw an error asynchronously in RxJs?

I have an observer that tracks questions and answers of a command line interface. What I would like to do is inject an error into the observer given a certain event in my code in order to terminate the observer and its subscription downstream. It is unknown at what time it runs.
I've tried throwing errors from a merge of a subject and the observable but I cannot seem to get anything out of it.
Here is the relevant code:
this.errorInjector$ = new Subject<[discord.Message, MessageWrapper.Response]>();
....
this.nextQa$ = merge(
nextQa$,
this.errorInjector$.pipe(
tap((): void => {
throw (new Error('Stop Conversation called'));
}),
),
);
// start conversation
Utils.logger.trace(`Starting a new conversation id '${this.uuid}' with '${this.opMessage.author.username}'`);
}
getNextQa$(): Observable<[discord.Message, MessageWrapper.Response]> {
return this.nextQa$;
}
stopConversation(): void {
this.errorInjector$.next(
null as any
);
}
The this.nextQa$ is merged with the local nextQa$ and the errorInjector$. I can confirm that stop conversation is being called and downstream is receiving this.nextQa$ but I am not seeing any error propagate downstream when I try to inject the error. I have also tried the this.errorInjector.error() method and the map() operator instead of tap(). For whatever reason I cannot get the two streams to merge and to throw my error. To note: this.nextQa$ does propagate errors downstream.
I feel like I am missing something about how merge or subjects work so any help or explanation would be appreciated.
EDIT
Well I just figured out I need a BehaviorSubject instead of a regular subject. I guess my question now is why do I need a BehaviorSubject instead of a regular Subject just to throw an error?
EDIT 2
BehaviorSubject ALWAYS throws this error which is not what I want. It's due to the nature of its initial emission but I still don't understand why I can't do anything with a regular subject in this code.
First of all if you want subject to work you will have to subscribe before the error anyting is emitted. So there is a subscription sequence problem within your code. If you subscribe immediately after this.nextQa$ is created you shouldn't miss the error.
this.nextQa$ = merge(
nextQa$,
this.errorInjector$.pipe(
tap((): void => {
throw (new Error('Stop Conversation called'));
}),
),
);
this.nextQa$.subscribe(console.log,console.error)
The problem is getting the object with the stopConversation(): void from the dictionary object I have. The this object is defined and shows errorInjector$ is defined but the debugger tells me that errorInjector$ has become undefined when I hover over the value. At least that's the problem and I'll probably need to ask another question on that.

UnhandledPromiseRejectionWarning when using Fluture `encaseP` on `fetch`

I have just started using Flutures, and I am trying to fetch some remote data for a visualization with d3.
I created a function which accepts a DOM selector (e.g. #my-chart) and a url (e.g. https://example.com/data.json).
If an error occurs when fetching the data, I have a unary function that shows an error message. If everything goes well, I have a unary function that draws the visualization. For the sake of simplicity, let's suppose that these functions are just console.error and console.log.
const fn = async (selector, url) => {
// convert fetch (which returns a Promise) into a function that
returns a Future
const fetchf = Future.encaseP(fetch);
fetchf(url)
.chain(res => Future.tryP(_ => res.json()))
.fork(console.error, console.log);
}
Apparently I am missing something when wrapping fetch in a Future, because I get this warning:
UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch().
If I had to use async/await I would write something like this, which would not give me any warning.
const fn = async (selector, url) => {
let res;
try {
res = await fetch(url);
} catch (err) {
console.error(err);
return;
}
let data;
try {
data = res.json();
} catch (err) {
console.error(err);
return;
}
console.log(data);
};
It seems two things are going on here:
The data.json() function is not supposed to be wrapped inside tryP, because according to your second not broken example, it returns synchronously (there is no await). This would cause Fluture to raise a TypeError (because it expects to see a Promise, but gets a JSON value). Although, knowing the fetch API, data.json() typically does return a Promise, so it might also be your second example is broken, and something else is going on. Whatever it is, I suspect that an unexpected Error is being thrown somewhere. Do you see any other error messages in your console, besides the one you posted?
I did some testing, and it does seems to be true - when Fluture raises or catches a TypeError after a successful encaseP, it seems the original Promise manages to catch that error, and trigger the unhandled rejection. This appears to be a regression bug in Fluture, and I will be fixing it soon. In the meantime, if we get to the bottom of what's throwing your error, you will be able to continue without depending on said fix.
EDIT: I've opened a PR to fix the second issue: https://github.com/fluture-js/Fluture/pull/310
EDIT2: The fix has been released under version 10.3.1. Using that version should give you more insights in what's happening with issue 1.

Clarification about RxJS way of handling errors: "The observable data type disallows the exception from leaking from the stream's context"

I am in reference to the RxJS in action book by Manning publisher which gives the following sample code to demonstrate the RxJS way of handling errors:
const computeHalf = x => Math.floor(x / 2);
Rx.Observable.of(2,4,5,8,10)
.map(num => {
if(num % 2 !== 0) {
throw new Error(`Unexpected odd number: ${num}`); //#A
}
return num;
})
.map(computeHalf)
.subscribe(
function next(val) {
console.log(val);
},
function error(err) {
console.log(`Caught: ${err}`); //#B
},
function complete() {
console.log('All done!');
}
);
The book goes on to say about RxJS:
errors don't escape the observable pipeline.
and also puts it differently:
The observable data type disallows the exception from leaking from the stream's context.
Can someone please explain how this differs (as far as side-effects are concerned) from handling errors with a traditional try/catch as follows?
try {
someDangerousFunction();
}
catch(error){
console.log(error.message);
}
How the latter sample causes a side-effect compared to the former?
What is meant by the two quotes above?
This means that when you set an error handler when calling subscribe(.., error => ...) the exception is only passed to the error handler and nothing else (you threw an exception but it was caught by RxJS).
That's what is meant by "errors don't escape the observable pipeline." and by "leaking" I think they mean the same.
In the example you posted you're throwing an error inside map() which is caught by map() automatically and sent as an error notification.
So you don't typically wrap Observable chains with try/catch. One important thing to notice is that if your subscribe call doesn't set and error handler than the error is rethrown which might break your application. In such case you might want to use try/catch but it's always easier to use the error handler instead.
I think it is meant to be contrasted with Promise and not so much with try/catch. When coding against Promise a common error would be...
doSomeAsyncTask().then(rsp => {
doSomeOtherAsyncTask(rsp).then(rsp2 => {
myButton.label = rsp2.text;
});
}).catch(err => {
console.log(err);
});
One might think that they are catching errors but if some error is thrown by doSomeOtherAsyncTask it wouldn't actually propagate. Of course, there are ways to avoid this but it is a pretty easy error to make if you are new to promises.

Rebus: Is it possible to have mulitple Sagas with the same IHandleMessages type

Is it possible to have two or more Sagas that handle the same message type?
For example two sagas that both implement IHandleMessages?
Does this work for all saga storages or only some of them?
Update:
I've tested I have two sagas (SendSMSSaga and SendEmailSaga) both Implements the same IHandleMessages (seperate IAmInitiatedBy commands) the only thing that happens in the first handle is:
if (!IsNew) return;
Data.Command = message;
Data.Id = message.SagaId ?? Guid.NewGuid();
Data.Status = Status.INIT;
Data.LogRecordId = Extensions.CreateLogRecordId();
await Bus.SendLocalWithHeader(new CreateLogCompleteCommand() { SagaId = Data.Id, LogRecordId = Data.LogRecordId });
//SendSMSSaga
protected override void CorrelateMessages(ICorrelationConfig config)
{
config.Correlate(x => x.SagaId, y => y.Id);
config.Correlate(x => x.SagaId, y => y.Id);
}
//SendEmailSaga
protected override void CorrelateMessages(ICorrelationConfig config)
{
config.Correlate(x => x.SagaId, y => y.Id);
config.Correlate(x => x.SagaId, y => y.Id);
}
Then I get the following exception:
5 unhandled exceptions: 13.07.2016 10:26:30 +02:00: System.ArgumentException: Object of type 'Unipluss.Sign.Notification.Queue.Saga.Email.SendEmailSagaData' cannot be converted to type 'Unipluss.Sign.Notification.Queue.Saga.SendSMSSagaData'.
Any tips on what I'm doing wrong?
I've tried both the SQL and the new AzureStorage saga implementations.
Yes it is possible, and it works for all saga storages(*).
You need to keep in mind though that each saga's data is updated separately, so if e.g. the last update experiences a ConcurrencyException, the message is rolled back and will be received again.
If this can cause issues for you, you should be sure to make your sagas idempotent.
(*) At the time when Rune asked the question it did NOT work as it should. There was a subtle bug in Rebus versions < 0.99.68 that would not include the saga data's type in the criteria when correlating by ID.
This would not be a problem in most cases, because it required multiple saga handlers to handle the same message in order to expose the bug.
It has been fixed in 0.99.68 for all the affected saga persisters.

Implementing exception handling in a function which returns a Stream

I'm implementing a function which returns a Stream. I'm not sure how to implement the error handling, what is best practice?
For functions which return a Future, it's best practice never to throw a synchronous error. Is this also true for functions which return a Stream?
Here's an example of what I'm thinking:
Stream<int> count() {
var controller = new StreamController<int>();
int i = 0;
try {
doSomethingThatMightThrow();
new Timer.repeating(new Duration(seconds: 1), () => controller.add(i++));
} on Exception catch (e) {
controller.addError(e);
controller.close();
}
return controller.stream;
}
In general it is true for Streams as well. The main idea is, that users should only need to handle errors in one way. Your example moves all errors to the stream.
There are circumstances where immediate errors are better (for instance you could make the error is due to a programming error and should never be handled anyways, or if you want to guarantee that a Stream never produces errors), but sending the error through a stream is almost always a good thing.
Small nit: a Stream should usually (there are exceptions) not produce any data until somebody has started listening. In your example you are starting a Timer even though you don't even know if there will ever be a listener. I'm guessing the example is reduced and not representative of your real code, but it is something to look out for. The solution would be to use the StreamController's callbacks for pause and subscription changes.
I've updated the example to take on-board Florian's comments.
In my real use case, I don't ever want to buffer the results, so I'm throwing an UnsupportedError if the stream is paused.
I've made it a terminating stream, rather than an infinite one.
If the user of this function adds a listener asynchronously after a few seconds, then they will lose the first couple of results. They shouldn't do this. I guess that's something to document clearly. Though perhaps, I could also throw an error if the subscribe state changes after the first data has been received, but before a close has been received.
Stream<int> count(int max) {
var controller = new StreamController<int>(
onPauseStateChange: () => throw new UnsupportedError('count() Stream pausing not supported.'));
int i = 0;
try {
doSomethingThatMightThrow();
new Timer.repeating(new Duration(seconds: 1), () {
if (!controller.hasSubscribers)
return;
controller.add(i++);
if (i >= max)
controller.close();
});
} on Exception catch (e) {
controller.addError(e);
controller.close();
}
return controller.stream;
}

Resources