Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Async Disposable.Create

Disposable.Create require an Action as parameter. The Action is run when the Rx subscription is being disposed.

When disposing a Rx subscription I’d like to run some asynchronous clean up code, however using async () => with Action is identical to async void, which I’d like to avoid. For more details on why I want to avoid this, see here.

Is it possible to create something like a Disposable.AsyncCreate, which accepts Func<Task> rather than Action. If so how should I use it as part of a CompositeDisposable?

Or are there other patterns for dealing with asynchronous Disposal?

like image 332
1iveowl Avatar asked Jul 24 '17 17:07

1iveowl


People also ask

What is DisposeAsync?

The DisposeAsyncCore() method is intended to perform the asynchronous cleanup of managed resources or for cascading calls to DisposeAsync() . It encapsulates the common asynchronous cleanup operations when a subclass inherits a base class that is an implementation of IAsyncDisposable.

What is IAsyncDisposable?

The IAsyncDisposable. DisposeAsync method of this interface returns a ValueTask that represents the asynchronous dispose operation. Classes that own unmanaged resources implement this method, and the consumer of these classes calls this method on an object when it is no longer needed.

What is await using C#?

The await operator suspends evaluation of the enclosing async method until the asynchronous operation represented by its operand completes. When the asynchronous operation completes, the await operator returns the result of the operation, if any.


1 Answers

You could do something like this. I'm still not sure how good an idea it is:

public class DisposableAsync
{
    private readonly IDisposable _disposable; 
    private readonly Func<Task> _asyncDisposalAction;
    public DisposableAsync(IDisposable disposable, Func<Task> asyncDisposalAction)
    {
        _disposable = disposable;
        _asyncDisposalAction = asyncDisposalAction;
    }

    public Task DisposeAsync()
    {
        _disposable.Dispose();
        return _asyncDisposalAction();
    }
}

public static class DisposableAsyncExtensions
{
    public static DisposableAsync ToAsync(this IDisposable disposable, Func<Task> asyncDisposalAction)
    {
        return new DisposableAsync(disposable, asyncDisposalAction);
    }
}

You could then use it like this:

async Task Go()
{

    var o = Observable.Interval(TimeSpan.FromMilliseconds(100));
    var d = o
        .Subscribe(i => Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: {i}"))
        .ToAsync(async () =>
        {
            Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: Dispose Beginning");
            await Task.Delay(1000);
            Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: Dispose Complete");
        });
    Console.Read();
    var t = d.DisposeAsync();
    Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: Outside task, waiting for dispose to complete");
    await t;
    Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: Task Complete");

}

This solution wouldn't work with using() statements, and the class DisposableAsync should be robustified. Outside of that, I can't think of anything wrong with it, but I'm predisposed (ahem) against it though. Just feels kind of hacky.

like image 160
Shlomo Avatar answered Oct 10 '22 21:10

Shlomo