/// <summary> /// Awaits for a given task while periodically calling <paramref name="action"/>. /// </summary> /// <typeparam name="TResult">Return type of the task</typeparam> /// <param name="task">The task to await</param> /// <param name="period">Period at which to call <paramref name="action"/></param> /// <param name="action">Action to periodically call. The action receives elapsed time since this method was called.</param> /// <param name="reportImmediately">Whether <paramref name="action"/> should be called immediately.</param> /// <param name="reportAtEnd">Whether <paramref name="action"/> should be called at when </param> /// <returns>The result of the task.</returns> public static async Task <TResult> AwaitWithProgressReporting <TResult>( Task <TResult> task, TimeSpan period, Action <TimeSpan> action, bool reportImmediately = true, bool reportAtEnd = true) { var startTime = DateTime.UtcNow; var timer = new StoppableTimer( () => { action(DateTime.UtcNow.Subtract(startTime)); }, dueTime: reportImmediately ? 0 : (int)period.TotalMilliseconds, period: (int)period.TotalMilliseconds); using (timer) { await task.ContinueWith(t => { return(timer.StopAsync()); }).Unwrap(); // report once at the end if (reportAtEnd) { action(DateTime.UtcNow.Subtract(startTime)); } return(await task); } }
/// <summary> /// Awaits given tasks, periodically calling <paramref name="action"/>. /// </summary> /// <typeparam name="TItem">Type of the collection to iterate over.</typeparam> /// <typeparam name="TResult">Type of the tasks' result.</typeparam> /// <param name="collection">Collection to iterate over.</param> /// <param name="taskSelector">Function to use to select a task for a given item from the given collection.</param> /// <param name="action"> /// Action to call periodically (as specified by <paramref name="period"/>). /// The action receives /// (1) total elapsed time, /// (2) all original items, and /// (3) a collection of non-finished items /// </param> /// <param name="period">Period at which to call <paramref name="action"/>.</param> /// <returns>The results of inidvidual tasks.</returns> public static async Task <TResult[]> AwaitWithProgressReporting <TItem, TResult>( IReadOnlyCollection <TItem> collection, Func <TItem, Task <TResult> > taskSelector, Action <TimeSpan, IReadOnlyCollection <TItem>, IReadOnlyCollection <TItem> > action, TimeSpan period) { var startTime = DateTime.UtcNow; var timer = new StoppableTimer( () => { var elapsed = DateTime.UtcNow.Subtract(startTime); var remainingItems = collection .Where(item => !taskSelector(item).IsCompleted) .ToList(); action(elapsed, collection, remainingItems); }, dueTime: 0, period: (int)period.TotalMilliseconds); using (timer) { var result = await Task.WhenAll(collection.Select(item => taskSelector(item))); await timer.StopAsync(); // report once at the end action(DateTime.UtcNow.Subtract(startTime), collection, CollectionUtilities.EmptyArray <TItem>()); return(result); } }