Skip to content

NotifyTask concern #19

Description

@sjb-sjb

As I mentioned in another post, I'm a fan of NotifyTask. Recently however I noticed, well, not exactly a problem but something unexpected and I would say somewhat inconvenient. I would suggest a change.

The overall design of NotifyTask is that you hand the task in to the constructor, and then you await TaskCompleted. Meanwhile the properties of NotifyTask, such as IsCompleted, IsFaulted, ErrorMessage should be notified by NotifyTask.

But suppose the task being handed in to Create is already faulted? There are a lot of ways this could arise. What will happen is that MonitorTaskAsync will run to completion in the constructor, including NotifyProperties. But since we are still in the constructor of NotifyTask, there are no handlers attached to PropertyChanged at that point. Then when you await TaskCompleted, it returns immediately since the Task that it refers to has already run to completion. Consequently no property notifications will ever be sent.

In effect, the properties of NotifyTask are being initialized to values different from the default value (i.e. some of them are true while the default value for bool is false) but no property notifications occur. This is, I would say, somewhat logical but not at all convenient. We can't just do this:

    asyncExecution = NotifyTask.Create<TOut>( methodAsync() );
    asyncExecution.PropertyChanged += AsyncExecution_PropertyChanged;
    await asyncExecution.TaskCompleted; 

and define a handler

    private void AsyncExecution_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(NotifyTask.InnerException)) {
            this.HandleException( ((NotifyTask)sender).InnerException);
        }
    }

Instead, in addition to doing all the above we also have to test for asyncExecution.InnerException != null after the call to Create. This basically leads to duplicating the logic of the PropertyChanged handler, which is not very desirable from a design point of view.

Also the fact that you have to do this is not very discoverable, as I discovered the hard way!

To remedy this situation my suggestion is to change MonitorTaskAsync as follows:

    private async Task MonitorTaskAsync(Task task)
    {
        try {
            if (task.IsCompleted) {
                await Task.Yield();
            }
            await task;
        } catch {
        } finally {
            NotifyProperties(task);
        }
    }

This way, MonitorTaskAsync will definitely not run to completion. Instead it will always return a meaningful Task that when awaited will do the appropriate NotifyProperties.

Note, this will give you a somewhat different flow of control from the current implementation. The Create call will now always yield while in the past it would only yield if the task was not already completed.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions