File: Dispatching\DispatcherExtensions.cs
Web Access
Project: src\src\Core\src\Core.csproj (Microsoft.Maui)
using System;
using System.Threading;
using System.Threading.Tasks;
 
namespace Microsoft.Maui.Dispatching
{
	/// <summary>
	/// This class provides a set of extension methods that can be used on objects implementing <see cref="IDispatcher"/>.
	/// </summary>
	public static class DispatcherExtensions
	{
		/// <summary>
		/// Schedules the provided callback on the UI thread from a worker thread, and returns the results asynchronously.
		/// </summary>
		/// <typeparam name="T">The type returned from this method.</typeparam>
		/// <param name="dispatcher">The <see cref="IDispatcher"/> instance this method is called on.</param>
		/// <param name="func">The method to be executed by the dispatcher.</param>
		/// <returns>A <see cref="Task{TResult}"/> object containing information about the state of the dispatcher operation.</returns>
		public static Task<T> DispatchAsync<T>(this IDispatcher dispatcher, Func<T> func)
		{
			var tcs = new TaskCompletionSource<T>();
 
			dispatcher.Dispatch(() =>
			{
				try
				{
					var result = func();
					tcs.SetResult(result);
				}
				catch (Exception ex)
				{
					tcs.SetException(ex);
				}
			});
 
			return tcs.Task;
		}
 
		/// <summary>
		/// Schedules the provided action on the UI thread from a worker thread.
		/// </summary>
		/// <param name="dispatcher">The <see cref="IDispatcher"/> instance this method is called on.</param>
		/// <param name="action">The method to be executed by the dispatcher.</param>
		/// <returns><see cref="Task"/>.</returns>
		public static Task DispatchAsync(this IDispatcher dispatcher, Action action) =>
			dispatcher.DispatchAsync(() =>
			{
				action();
				return true;
			});
 
		/// <summary>
		/// Schedules the provided function on the UI thread from a worker thread.
		/// </summary>
		/// <typeparam name="T">The type returned from this method.</typeparam>
		/// <param name="dispatcher">The <see cref="IDispatcher"/> instance this method is called on.</param>
		/// <param name="funcTask">The function to be executed by the dispatcher.</param>
		/// <returns>A <see cref="Task{TResult}"/> object containing information about the state of the dispatcher operation.</returns>
		public static Task<T> DispatchAsync<T>(this IDispatcher dispatcher, Func<Task<T>> funcTask)
		{
			var tcs = new TaskCompletionSource<T>();
 
			dispatcher.Dispatch(async () =>
			{
				try
				{
					var ret = await funcTask().ConfigureAwait(false);
					tcs.SetResult(ret);
				}
				catch (Exception e)
				{
					tcs.SetException(e);
				}
			});
 
			return tcs.Task;
		}
 
		/// <summary>
		/// Schedules the provided function on the UI thread from a worker thread.
		/// </summary>
		/// <param name="dispatcher">The <see cref="IDispatcher"/> instance this method is called on.</param>
		/// <param name="funcTask">The function to be executed by the dispatcher.</param>
		/// <returns><see langword="Task"/>.</returns>
		public static Task DispatchAsync(this IDispatcher dispatcher, Func<Task> funcTask) =>
			dispatcher.DispatchAsync(async () =>
			{
				await funcTask().ConfigureAwait(false);
				return true;
			});
 
		/// <summary>
		/// Gets the synchronization context for the current thread.
		/// </summary>
		/// <param name="dispatcher">The <see cref="IDispatcher"/> instance this method is called on.</param>
		/// <returns>A <see cref="SynchronizationContext"/> object representing the current synchronization context.</returns>
		public static Task<SynchronizationContext> GetSynchronizationContextAsync(this IDispatcher dispatcher) =>
			dispatcher.DispatchAsync(() => SynchronizationContext.Current!);
 
		/// <summary>
		/// Starts a timer on the specified <see cref="IDispatcher"/> context.
		/// </summary>
		/// <param name="dispatcher">The <see cref="IDispatcher"/> instance this method is called on.</param>
		/// <param name="interval">Sets the amount of time between timer ticks.</param>
		/// <param name="callback">The callback on which the dispatcher returns when the event is dispatched.
		/// If the result of the callback is <see langword="true"/>, the timer will repeat, otherwise the timer stops.</param>
		/// <exception cref="ArgumentNullException">Thrown when <paramref name="callback"/> is <see langword="null"/>.</exception>
		public static void StartTimer(this IDispatcher dispatcher, TimeSpan interval, Func<bool> callback)
		{
			_ = callback ?? throw new ArgumentNullException(nameof(callback));
 
			var timer = dispatcher.CreateTimer();
			timer.Interval = interval;
			timer.IsRepeating = true;
			timer.Tick += OnTick;
			timer.Start();
 
			void OnTick(object? sender, EventArgs e)
			{
				if (!callback())
				{
					timer.Tick -= OnTick;
					timer.Stop();
				}
			}
		}
	}
}