I have a lot of code that is reactive but needs to call into Task based methods.
For example, in this snippet PracticeIdChanged is an IObservable. When PracticeIdChanged fires, I want the system to react by reloading some stuff and have code that looks like this:
PracticeIdChanged.Subscribe(async x => {
SelectedChargeInfo.Item = null;
await LoadAsync().ConfigureAwait(false);
});
Although it seems to work ok, I get warnings about executing async code in the Subscribe. Additionally, I consider this to be a code smell as I am mixing two separate threading models which I think may come to bite me later.
Refactoring like this works (even) with no combination methods like .Merge(), .Switch() or .Concat()
PracticeIdChanged
.Do(_ => SelectedChargeInfo.Item = null)
.Select(_ => LoadAsync().ToObservable())
.Subscribe();
When PracticeIdChanged fires the LoadAsync method executes
The Select results in an IObservable<IObservable> which looks odd. Is this ok or does it require some combination function like .Merge or .Switch()
In many places, I use SelectMany to execute the Task based method but it requires returning Task which would require changing the signature of the Task based method in the example above which I do not want to do.
It depends on what kind of notifications you expect to get from the resulting sequence, and what kind of behavior you want in case of errors. In your example you .Subscribe() to the sequence without passing any handler whatsoever (onNext/onError/onCompleted), indicating that you are not interested to be notified for anything. You don't care about the completion of the asynchronous operations, all of them becoming essentially fired-and-forgotten. Also a failure of one asynchronous operation will have no consequence to the rest: the already started asynchronous operations will continue running (they won't get canceled), and starting new asynchronous operations will not be impeded. Finally a failure of the source sequence (PracticeIdChanged) will result in an unhandled exception, that will crash the process. If that's the behavior that you want, then your current setup is what you need.
For comparison, let's consider this setup:
await PracticeIdChanged
.Do(_ => SelectedChargeInfo.Item = null)
.Select(_ => Observable.FromAsync(ct => LoadAsync(ct)))
.Merge()
.DefaultIfEmpty();
This setup assumes that the LoadAsync method has a CancellationToken parameter. The resulting sequence is awaited. The await will complete when all LoadAsync operations have completed, or any one of them has failed, or if the source sequence has failed. In case of failure, all currently running asynchronous operations will receive a cancellation signal, so that they can bail out quickly. The await will not wait for their completion though. Only the first error that occurred will be propagated as an exception. This exception can be handled by wrapping the await in a try/catch block. There is no possibility for an uncatchable, process-crashing, unhandled exception.
The purpose of the DefaultIfEmpty at the end of the chain is to prevent an InvalidOperationException, in case the source sequence emits zero elements. It's a workaround for this strange "feature" of empty observable sequences, to throw when waited synchronously or asynchronously.
Related
For some heavy caching / high performance path in my project I need to "cast" a Task<X> to a Task<Y> type - see here.
I used the ContinueWith solution, but I noticed that even when my original task is in a RanToCompletion state, the ContinueWith still returns a Task that is WaitingToRun.
var task = Task.FromResult("cheese");
var test = task.ContinueWith(x => (object)x);
Console.WriteLine(test.Status); // WaitingToRun
Console.ReadLine();
For reasons out of the scope of this question, this gives me some extra cost further down the line.
If I do the following, my very specific problem seems to be fixed and it will directly return a CompletedTask, otherwise it does the normal logic.
if ( task.IsCompletedSuccessfully )
return Task.FromResult((object)task.Result);
else
return task.ContinueWith(x => (object)x);
Is it safe to add this "happy path" to my logic?
Because this path is not in the normal ContinueWith logic I am afraid I am missing something and this will bite me when running in production in a high concurrent environment.
As is the case just about anytime ContinueWith comes up, the answer is to use await instead. Using ContinueWith correctly is really hard, and it's almost never the most effective solution to any given problem.
You've found one problem with ContinueWith, namely that without being really careful with your scheduler (or manually checking if the task is completed) you've ended up forcing work to be scheduled into a thread pool thread to run later when it just doesn't need to be. (That won't always happen, so you can't rely on that behavior, but it's possible, which is frankly, worse.)
Additionally, your ContinueWith code doesn't handle errors/cancellation properly. In your code cancelled tasks become errored instead, and errored tasks have their exception wrapped in an AggregateException.
Your code also has a bug in that you don't get the result from the task before casting it, and thus the result of your task is another task cast to an object, but that part is easily fixed by just adding in a Result call.
Here is a pre-await solution attempting to solve these problems, but in a post-await world, there's just no need.
public static async Task<TResult> Cast<TSource, TResult>(
this Task<TSource> task)
{
return (TResult)(object) await task.ConfigureAwait(false);
}
Not only is await a more effective syntax for scheduling continuations, but it's default behavior is much more likely to be correct in any given situation than the default behaviors of ContinueWith. It'll check if the task is completed and only schedule a continuation to run later if it's not, it doesn't wrap exceptions in an AggregateException, etc. The only default behavior that we don't want here is that by default await maintains the current synchronization context, which is often correct, but happens to not be needed for our method here.
I’ve got new question about one (or two) Reactive methods.
In my scenario I needed an observable sequence capable of suppressing other emitted Tasks while the first Task wasn’t completed, and ended up with something like this:
Observable.Interval(TimeSpan.FromMilliseconds(200))
.Select(x => Observable.FromAsync(async () =>
{
await Task.Delay(1000);
// Simulating long running task
Console.WriteLine(x);
}))
.Publish(x => x.FirstAsync().SelectMany(c => c).Repeat())
.Subscribe();
I tried to Google but I really can’t explain few things:
First of all, how that works 😂?
What is exactly on that Reactive sequence that blocks the observable from reaching the subscription?
What exactly Replay does in that? Isn’t Replay supposed to replay the Task in this case? Or I don’t know.
Can anyone explain detailed every step in that Reactive query?
What does Publish with that kind of selector. How Replay is playing in that query? And why do I need to call SelectMany on FirstAsync if only one element will be emitted anyway.
The .SelectMany(c => c) is an idiomatic way to flatten/merge a nested sequence. You can replace it with .Merge(), and the behavior of the query will be identical.
The Publish operator, when used with a Func<IObservable<TSource>, IObservable<TResult>> parameter, subscribes to the query on which it is chained, and then remains subscribed until the sequence produced by the lambda completes. So in your case, by wrapping the inner sequence x.FirstAsync().SelectMany(c => c).Replay() in a Publish, you delay the unsubscription from the chained sequence (the Interval+Select+FromAsync) until the inner sequence completes. The inner sequence never completes, so the chained sequence keeps forever producing one cold IObservable<Unit> subsequence every second. You can observe this happening, by intercepting a Do operator before the Publish:
.Do(x => Console.WriteLine($"New subsequence: {x.GetType().Name}"))
The Replay operator is similar to the Publish, with the difference that the Replay has memory of past notifications, whilst the Publish has no memories whatsoever. I guess that your intention was to attach the Repeat instead of the Replay. The Replay without parameter produces a "connectable" observable, that doesn't subscribe automatically to the chained sequence. You have either to Connect it manually, or to attach the RefCount operator to it. In your case you are doing neither, so the resulting sequence never emits anything and never completes. It's a nasty dead-lock situation.
I need to know are there any differences in these records and if so, what are they?
It`s quite difficult for me:
1) Task.Run(async () => { await CheckVerification(); });
2) Task.Run(() => CheckVerification());
3) await Task.Run(async () => { await CheckVerification(); });
Maybe it is easier to understand the difference if we forget about Task.Run for a second. The difference between your 1st and 2nd points is basically the same as awaiting or just returning an inner task from a Task returning method:
// 1)
public async Task WithAwait() => await SomeOtherTaskReturningMethod();
// 2)
public Task WithoutAwait() => return SomeOtherTaskReturningMethod();
The first one creates another task, which wraps the inner one, which is a small overhead but otherwise these two methods are functionally almost identical. A small difference is when SomeOtherTaskReturningMethod throws an exception, then the inner task's AggregatedException will be unwrapped, and the (first) inner exception will be thrown further. Of course, things still depend on what happens to the external task. In your 3rd point it is also awaited so you will get the unwrapped exception from the outer task (if any), while the 1st and 2nd examples are just fire and forget.
Now let's consider Task.Run again. The main question why does this method have overloads, which accept Tasks (actually Task returning callbacks), when you can also simply call await CheckVerification(); and get the same result? (I know it was not part of the question but it may worth to clarify this)
So the reason of such Task.Run overloads exist is that a Task returning method itself is not necessarily executed on another thread (I suggest this reading). For example, it can just send a message through a network socket and returning a Task, which will be completed when a specific answer is received. This makes the operation async, still not multi-threaded.
If you have such a task and you want force it to be executed on another thread, then you can execute it by Task.Run, which uses the default scheduler and executes your task on a pool thread eventually.
It is worth to mention though, that this is quite a rare scenario. In most cases you should rely on the internal implementation of the Task returning methods.
TL;DR:
So I think the real question is whether
await CheckVerification();
or
await Task.Run(() => CheckVerification());
is the better solution (or maybe one of them without await, which is fire and forget mode). In most cases I would vote for the first one but if you are really confident that the task returned by CheckVerification must be assigned to a pool thread (regardless whether it does that inside or not), then the second option also can be justified.
1) Starts a task. That task will wait for the method within, but asynchronously, so your UI or calling thread won't block.
2) Starts a task, which starts another task. Everything runs asynchonously, nothing gets awaited, completely fire and forget.
3) Will start a task, that task starts a method and awaits it, giving back execution context, letting the calling thread give away the execution context with its await, letting the method calling this continue execution until it comes to awaiting this method.
I thought I was being clever in finding a way to call an aync method in a ctor:
public AppStateModel(IBranchClient branchClient)
{
_branchClient = branchClient;
var loadBranch = new Action(async () =>
{
DataProviderReadResult<BranchDetailViewModel> result = await _branchClient.ReadOneItemAsync(AppSettings.BranchId, _initCts.Token);
});
loadBranch();
}
But the body of the action throws an exception, which I log and re-throw with plain throw;, but this ctor executes fine and the rest of my code continues running as if nothing happens. Why is this?
Because the action is asynchronous. loadBranch() returns as soon as the first await is reached, and can't possibly throw the exception you're expecting - that's part of the information in the Task that you've ignored (by using Action instead of Func<Task>).
All in all, you've just written a more obfuscated version of this:
_branchClient.ReadOneItemAsync(AppSettings.BranchId, _initCts.Token);
.NET constructors are inherently synchronous. They shouldn't be doing anything where you'd benefit from using asynchronous code - and it's a good idea to do as little as possible in a constructor (or methods called by the constructor). If you need complex actions, asynchronous code, I/O, lots of CPU work... use a static method. And since you're running asynchronous code, make it return a Task<AppStateModel>, fitting properly in the whole asynchronous flow.
Also note that the exception will not be swallowed on older .NET runtimes. Assuming there's no synchronization context, the exception is still thrown on a background thread (where the continuation to the asynchronous operation is posted) - and the default for unhandled exceptions on thread-pool threads used to be "bring down the whole application". This would happen when the Task object was being finalized, so decoupled from all your program logic, pretty much random as far as you can tell. After all, what else can you do - there's no good place where the exception could be observed, and the only root there ever was basically said "I don't care about what happens with this task". But given how complicated it was to ensure that every single exception is properly observed and handled, the default was changed to "unobserved exceptions are ignored".
Let's deconstruct what happens here: You have an Action delegate that points to an anonymous async void method. What does async void mean? It means that the method actually returns a Task that encapsulates the async logic.
What it means is that when you call loadBranch, it executes an async method. When the async method hits the await call, it returns a Task object that allows you to wait on it, add a continuation, or whatever. But since you don't have an explicit Task variable to capture it, you just let your constructor go out of scope, without any code handling the continuation of the Task. This means that when the Task throws, the ctor has already exited.
I have the following method:
public async Task DeleteAmendment(int amendmentHeaderId, int userId)
{
// Delete the corresponding version records.
await _amendmentVersionService.DeleteForAmendmentAsync(amendmentHeaderId);
// Delete the corresponding lifecycle records.
await _amendmentLifecycleService.DeleteForAmendmentAsync(amendmentHeaderId);
// Delete the amendment header record itself.
await _amendmentHeaderService.DeleteAsync(amendmentHeaderId, userId);
}
I am trying to verify that the methods are called in order.
I have tried setting callbacks (see below)
AmendmentVersionService.Setup(x => x.DeleteForAmendmentAsync(It.IsAny<int>()))
.Callback(() => ServiceCallbackList.Add("AmendmentVersionService"));
AmendmentLifecycleService.Setup(x => x.DeleteForAmendmentAsync(It.IsAny<int>()))
.Callback(() => ServiceCallbackList.Add("AmendmentLifecycleService"));
AmendmentHeaderService.Setup(x => x.DeleteAsync(It.IsAny<int>(), It.IsAny<int>()))
.Callback(() => ServiceCallbackList.Add("AmendmentHeaderService"));
But the list only contains the string "AmendmentVersionService"
Any ideas?
One way to achieve the same goal, with a different concept would be to have 3 tests, one per call. It's a bit dirty, but as a fallback solution, it could get you out of the woods
for the first call:
Setup call 2 to throw an exception of a custom type TestException.
assert only call 1 was performed
Expect the TestException to be thrown
for the second call:
Setup call 3 to throw an exception of a custom type TestException.
assert call 1 and 2 were performed
Expect the TestException to be thrown
for the third call:
Setup all calls to perform normally.
assert call 1, 2 and 3 were performed
You could use continuations (below) but really if you need to garuntee that these things happen in order then they should not be async operations. Typically you would want the async operations to be able to run at the same time;
public async Task DeleteAmendment(int amendmentHeaderId, int userId)
{
Task.Run(() =>
{
// Delete the corresponding version records.
await _amendmentVersionService.DeleteForAmendmentAsync(amendmentHeaderId);
}).ContinueWith(_ =>
{
// Delete the corresponding lifecycle records.
await _amendmentLifecycleService.DeleteForAmendmentAsync(amendmentHeaderId);
}).ContinueWith(_ =>
{
// Delete the amendment header record itself.
await _amendmentHeaderService.DeleteAsync(amendmentHeaderId, userId);
});
}
Your problem is that you will never be able to know if a method was performed as a result of the previous one finishing (awaited) or if you were lucky enough not to suffer from a race condition (call made without await, or no ContinueWith)
The only way you can actually test it for sure is by replacing the default TaskScheduler by an implementation of your own, which will not queue the subsequent task. If the subsequent task gets called, then your code is wrong. If not, that means that the task is really executed as a result of the previous one completing.
We have done this in Testeroids, a test framework a friend and I built.
by doing so, your custom TaskScheduler can perform the tasks sequentially, in a single thread (to really highlight the timeline problems you could have) and record which tasks were scheduled and in which older.
It will require a lot of effort on your part if you want to be that thorough, but at least you get the idea.
In order to replace the defaukt TaskScheduler, you can get inspired by the work we did on Testeroids.
https://github.com/Testeroids/Testeroids/blob/c5f3f02e8078db649f804d94c37cdab3df89fed4/solution/src/app/Testeroids/TplTestPlatformHelper.cs
Thanks to Stephen Brickner ...
I made all my calls synchronous which made the callbacks in the Moq's work like a dream.
Thanks for all your help much appreciated!