简体   繁体   中英

Use an RX observable to create a “debounce” effect

I've got an FTP client that I want to leave connected to the FTP server, unless (say) a minute passes with no activity. I'd like to do this using an Observable.

Here's a very dumbed-down Linqpad script that demonstrates the concept:

async Task Main()
{
    var client = new Client();
    client.Connect();

    var debounce = new Subject<int>();
    debounce
        .Throttle(TimeSpan.FromSeconds(1))
        .Subscribe(eventNumber => client.Disconnect(eventNumber));

    // Something uses the FTP client
    debounce.OnNext(1);
    await Task.Delay(200);

    // Something else uses the FTP client
    debounce.OnNext(2);
    await Task.Delay(300);

    // No activity, the client will disconnect
    await Task.Delay(1000);
}

public class Client
{
    public void Connect() => Console.WriteLine("Connected");
    public void Disconnect(int eventNumber) => Console.WriteLine($"Disconnected: {eventNumber}");
}

This works perfectly - the client disconnects after event "2".

Question : Is there a better way to do this? Or more accurately, is there a better way to do this without using the Subject ?

Edit

Here's a more fleshed-out version of the class - effectively, it is subscribed to an observable which will tell it some files that need to be downloaded; if no files come through for some timeout, then I want the client to disconnect.

public class MyClassThatDownloadsViaFtp
{
    private IObserver<Unit> _debouncer;
    private FtpClient _client;

    public MyClassThatDownloadsViaFtp(IObservable<FileToDownload> filesToDownloadViaFtp)
    {
        filesToDownloadViaFtp.Subscribe(DownloadFileViaFtp);

        // Disconnect after a minute of activity
        _debouncer = new Subject<Unit>();
        _debouncer
            .Throttle(TimeSpan.FromMinutes(1))
            .Subscribe(_ => DisconnectFtpClient());
    }

    public void DownloadFileViaFtp(FileToDownload file)
    {
        if (_client == null) _client = ConnectFtpClient();

        // Signal that the client is doing some work to prevent disconnect
        _debouncer.OnNext(Unit.Default);
        _client.Download(file.PathOnFtpServer);
    }

    // implementation irrelivent
    private FtpClient ConnectFtpClient() => new FtpClient();
    private FtpClient DisconnectFtpClient() => _client = null;
}

I figured out that since I have a source stream, it's probably easier to throttle it to achieve the same effect (as follows); however, I'd still like to know the best way to do this in cases where I do not have a source stream that I can throttle.

public class MyClassThatDownloadsViaFtp 
{
    private FtpClient _client;

    public MyClassThatDownloadsViaFtp(IObservable<FileToDownload> filesToDownloadViaFtp)
    {
        filesToDownloadViaFtp
            .Select(DownloadFileViaFtp)
            .Throttle(TimeSpan.FromMinutes(1))
            .Subscribe(_ => DisconnectFtpClient());
    }

    public Unit DownloadFileViaFtp(FileToDownload file)
    {
        if (_client == null) _client = ConnectFtpClient();
        _client.Download(file.PathOnFtpServer);

        return Unit.Default;
    }

    // implementation irrelivent
    private FtpClient ConnectFtpClient() => new FtpClient();
    private FtpClient DisconnectFtpClient() => _client = null; 
}

You basically answered your question with this:

public MyClassThatDownloadsViaFtp(IObservable<FileToDownload> filesToDownloadViaFtp)
{
    filesToDownloadViaFtp
        .Select(DownloadFileViaFtp)
        .Throttle(TimeSpan.FromMinutes(1))
        .Subscribe(_ => DisconnectFtpClient());
}

If you don't have a convenient stream like filesToDownloadViaFtp then create one from either Observable.Create or Observable.FromEvent , or Observable.FromEventPattern , etc..

One quibble: Select is ideally run with no side-effects and DownloadFileViaFtp is very much a side-effect. Side-effects are best in a Subscribe call.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM