Skip to content

Instantly share code, notes, and snippets.

@victorjonsson
Last active September 9, 2024 19:12
Show Gist options
  • Save victorjonsson/692f9eaea2805052dfba60053f9b66af to your computer and use it in GitHub Desktop.
Save victorjonsson/692f9eaea2805052dfba60053f9b66af to your computer and use it in GitHub Desktop.
Syntactic sugar for handling async processes in the background, when you don't need or want to await the results
namespace TestingStuff.Tasks
{
/// <summary>
/// Syntactic sugar for handling running async processes in the background, without await
/// </summary>
/// <example>
/// <code>
/// BackgroundTaskRunner.RunOnce(() => { });
/// BackgroundTaskRunner.Run(async () => { }).EveryMinute();
/// BackgroundTaskRunner.Run(async () => { }).Every(2).Minutes();
/// BackgroundTaskRunner.Run(async () => { }).Every(10).Seconds();
///
/// # with "when" conditions
/// BackgroundTaskRunner.When(() => true).RunOnce(async () => { });
/// BackgroundTaskRunner.When(() => true).Run(async () => { }).Every(100).Seconds();
///
/// # With "until" conditions
/// BackgroundTaskRunner.Run(async () => { }).Until(() => false).EveryMinute();
/// BackgroundTaskRunner.Run(async () => { }).Until(() => false).Every(10).Minutes();
///
/// # All in one...
/// BackgroundTaskRunner
/// .When(() => true)
/// .Run(async () => { })
/// .Until(() => false)
/// .Every(10).Minutes();
/// </code>
/// </example>
public class BackgroundTaskRunner
{
public static EveryStepSupportingUntil Run(Action callback)
{
return new EveryStepSupportingUntil(null, null)
{
SyncCallback = callback
};
}
public static EveryStepSupportingUntil Run(Func<Task> callback)
{
return new EveryStepSupportingUntil(null, null)
{
AsyncCallback = callback
};
}
public static void RunOnce(Action action)
{
_ = Task.Run(action);
}
public static WhenStep When(Func<bool> condition)
{
return new WhenStep(condition);
}
public class WhenStep
{
private Func<bool> _condition;
public WhenStep(Func<bool> condition)
{
_condition = condition;
}
public void RunOnce(Action callback)
{
new EveryStepSupportingUntil(_condition, null)
{
SyncCallback = callback
}
.EveryMinute();
}
public EveryStepSupportingUntil Run(Action callback)
{
return new EveryStepSupportingUntil(_condition, null)
{
SyncCallback = callback
};
}
}
public class EveryStepSupportingUntil: EveryStep
{
public Action? SyncCallback { get; set; }
public Func<Task>? AsyncCallback { get; set; }
private readonly Func<bool>? _condition;
private readonly Func<bool>? _untilCondition;
public EveryStepSupportingUntil(Func<bool>? condition, Func<bool>? untilCondition): base(condition, untilCondition)
{
}
public FinalRunStep Every(int value)
{
return new FinalRunStep(_condition, _untilCondition, value)
{
SyncCallback = SyncCallback,
AsyncCallback = AsyncCallback
};
}
public void EveryMinute()
{
new FinalRunStep(_condition, _untilCondition, 1)
{
SyncCallback = SyncCallback,
AsyncCallback = AsyncCallback
}.Minutes();
}
public EveryStep Until(Func<bool> untilCondition)
{
return new EveryStep(_condition, untilCondition)
{
SyncCallback = SyncCallback,
AsyncCallback = AsyncCallback
};
}
}
public class EveryStep
{
public Action? SyncCallback { get; set; }
public Func<Task>? AsyncCallback { get; set; }
protected readonly Func<bool>? _condition;
protected readonly Func<bool>? _untilCondition;
public EveryStep(Func<bool>? condition, Func<bool>? untilCondition)
{
_condition = condition;
_untilCondition = untilCondition;
}
public FinalRunStep Every(int value)
{
return new FinalRunStep(_condition, _untilCondition, value)
{
SyncCallback = SyncCallback,
AsyncCallback = AsyncCallback
};
}
public FinalRunStep EveryMinute()
{
return new FinalRunStep(_condition, _untilCondition, 1)
{
SyncCallback = SyncCallback,
AsyncCallback = AsyncCallback
};
}
}
public class FinalRunStep
{
public Action? SyncCallback { get; set; }
public Func<Task>? AsyncCallback { get; set; }
private readonly Func<bool>? _condition;
private readonly Func<bool>? _untilCondition;
private int _waitValue;
private string _waitUnit = "";
public FinalRunStep(Func<bool>? condition, Func<bool>? untilCondition, int value)
{
_condition = condition;
_untilCondition = untilCondition;
_waitValue = value;
}
public void Minutes()
{
_waitUnit = nameof(Minutes);
_ = Run();
}
public void Seconds()
{
_waitUnit = nameof(Seconds);
_ = Run();
}
private async Task Run()
{
var wait = _waitUnit == nameof(Minutes) ? (1000 * 60 * _waitValue) : (1000 * _waitValue);
while (true)
{
await Task.Delay(wait);
if (_untilCondition != null)
{
if (_untilCondition.Invoke())
{
break;
}
}
if (_condition == null)
{
await InvokeCallback();
} else
{
if (_condition.Invoke())
{
await InvokeCallback();
break;
}
}
}
}
private async Task InvokeCallback()
{
if (SyncCallback != null)
{
SyncCallback.Invoke();
} else
{
await AsyncCallback.Invoke();
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment