Cross-thread label countdown
Invoke is a blocking call that returns only after that call has competed.  

That means your loop is also including the time it takes to marshal over to the GUI thread and complete.  You probably don't want that.

I would use BeginInvoke instead, which does not wait for the method to complete on the GUI thread.

This is also the difference between SynchronizationContext methods Post and Send.

I would also prevent await from potentially capturing the current SynchronizationContext using ConfigureAwait(false).

To protect against exceptions if the control is disposed of (happens on form close and for other reasons) I'd add an IsDisposed check.

Finally I would allow this Task to be cancelled as a matter of best practices using a CancellationToken.

private async Task SnoozeAsync(int seconds, CancellationToken token)
{
    for (var i = 0; i &lt; seconds; i++)
    {
        if (token.IsCancellationRequested)
            break;
        BeginInvoke((MethodInvoker)(() =&gt; 
        {
            if (!statusLabel.IsDisposed)
                statusLabel.Text = $"Waiting {seconds - i} seconds...";
        }));
        await Task.Delay(1000, token).ConfigureAwait(false);
    }
}