Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ Task.FromResult("not-a-url") //

The `IfFulfilled` and `IfFaulted` methods can be used to perform side effects such as logging when the `Task<T>` is in the fulfilled or faulted state, respectively.

**NOTE**: These functions do _not_ trap errors. If the function passed to them throws an exception, the result is a faulted task with that exception.

```c#
HttpClient client; // Assuming this is coming from an HttpClientFactory or injected or whatever

Expand Down
15 changes: 5 additions & 10 deletions src/TaskExtensionsIfFaulted.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
using System;
using System.Threading;
using System.Threading.Tasks;

namespace RLC.TaskChaining;

using static TaskStatics;

public static partial class TaskExtensions
{
/// <summary>
Expand Down Expand Up @@ -39,13 +36,11 @@ public static Task<T> IfFaulted<T, R>(this Task<T> task, Func<Exception, Task<R>
Exception taskException = PotentiallyUnwindException(continuationTask.Exception!);

return Task.FromException<R>(PotentiallyUnwindException(continuationTask.Exception!))
.Catch<R>(ex => onFaulted(ex))
.Then(
_ => Task.FromException<T>(taskException),
_ => Task.FromException<T>(taskException)
);
.Catch(onFaulted)
.Then(_ => Task.FromException<T>(taskException));
}
else if (continuationTask.IsCanceled)

if (continuationTask.IsCanceled)
{
try
{
Expand All @@ -70,5 +65,5 @@ public static Task<T> IfFaulted<T, R>(this Task<T> task, Func<Exception, Task<R>
/// <param name="onFaulted">The function to execute if the task is faulted.</param>
/// <returns>The task.</returns>
public static Task<T> IfFaulted<T>(this Task<T> task, Func<Exception, Task> onFaulted)
=> task.IfFaulted<T, T>(exception => onFaulted(exception).ContinueWith(continuationTask => Task.FromException<T>(exception)).Unwrap());
=> task.IfFaulted<T, T>(exception => onFaulted(exception).ContinueWith(_ => Task.FromException<T>(exception)).Unwrap());
}
27 changes: 12 additions & 15 deletions src/TaskExtensionsIfFulfilled.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,24 @@ public static Task<T> IfFulfilled<T>(this Task<T> task, Action<T> consumer)
/// <param name="func">The function to execute if the task is fulfilled.</param>
/// <returns>The task.</returns>
public static Task<T> IfFulfilled<T, R>(this Task<T> task, Func<T, Task<R>> func)
=> task.ContinueWith(async continuationTask =>
=> task.ContinueWith(continuationTask =>
{
if (continuationTask.IsFaulted || continuationTask.IsCanceled)
{
return continuationTask;
}
else
{
T value = await continuationTask;

return Task.FromResult(value).Then(func).Then(_ => value, _ => value);
}
}).Unwrap().Unwrap();
return continuationTask.Then(value =>
{
func(value);
return value;
});
}).Unwrap();

/// <summary>
/// Executes a function and throws away the result if the <see name="Task{T}"/> is in a fulfilled state.
/// </summary>
/// <typeparam name="T">The task's underlying type.</typeparam>
/// <typeparam name="R">The type of the discarded result of <paramref name="func"/>.</typeparam>
/// <param name="task">The task.</param>
/// <param name="func">The function to execute if the task is fulfilled.</param>
/// <returns>The task.</returns>
Expand All @@ -53,13 +52,11 @@ public static Task<T> IfFulfilled<T>(this Task<T> task, Func<T, Task> func)
{
return continuationTask;
}
else

return continuationTask.Then(async value =>
{
return continuationTask.Then(async value =>
{
await func(value);
return value;
});
}
await func(value);
return value;
});
}).Unwrap();
}
109 changes: 90 additions & 19 deletions src/TaskExtensionsTap.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,37 @@
using System;
using System.Threading;
using System.Threading.Tasks;

namespace RLC.TaskChaining;

using static TaskStatics;

public static partial class TaskExtensions
{
/// <summary>
/// Executes a function and discards the result on a <see name="Task{T}"/> whether it is in a fulfilled or faulted state.
/// </summary>
/// <remarks>This method is useful if you need to perform a side effect without altering the <see name"Task{T}"/>'s
/// <remarks>This method is useful if you need to perform a side effect without altering the <see name="Task{T}"/>'s
/// value, such as logging.</remarks>
/// <typeparam name="T">The task's underlying type.</typeparam>
/// <param name="task">The task.</param>
/// <param name="onFulfilled">The function to execute if the task is fulfilled.</param>
/// <param name="onFaulted">The function to execute if the task is faulted.</param>
/// <returns>The task.</returns>
public static Task<T> Tap<T>(this Task<T> task, Action<T> onFulfilled, Action<Exception> onFaulted)
=> task.IfFulfilled(onFulfilled).IfFaulted(onFaulted);
=> task.ContinueWith(continuationTask =>
{
if (continuationTask.IsCanceled)
{
return continuationTask;
}

return continuationTask.IsFaulted
? continuationTask.IfFaulted(onFaulted)
: continuationTask.IfFulfilled(onFulfilled);
}).Unwrap();

/// <summary>
/// Executes a function and discards the result on a <see name="Task{T}"/> whether it is in a fulfilled or faulted state.
/// </summary>
/// <remarks>This method is useful if you need to perform a side effect without altering the <see name"Task{T}"/>'s
/// <remarks>This method is useful if you need to perform a side effect without altering the <see name="Task{T}"/>'s
/// value, such as logging.</remarks>
/// <typeparam name="T">The task's underlying type.</typeparam>
/// <typeparam name="R">The output type of the <paramref name="onFaulted" /> function.</typeparam>
Expand All @@ -33,25 +40,42 @@ public static Task<T> Tap<T>(this Task<T> task, Action<T> onFulfilled, Action<Ex
/// <param name="onFaulted">The function to execute if the task is faulted.</param>
/// <returns>The task.</returns>
public static Task<T> Tap<T, R>(this Task<T> task, Action<T> onFulfilled, Func<Exception, Task<R>> onFaulted)
=> task.IfFulfilled(onFulfilled).IfFaulted(onFaulted);
=> task.Tap(
value =>
{
onFulfilled(value);
return Task.FromResult(value);
},
onFaulted
);

/// <summary>
/// Executes a function and discards the result on a <see name="Task{T}"/> whether it is in a fulfilled or faulted state.
/// </summary>
/// <remarks>This method is useful if you need to perform a side effect without altering the <see name"Task{T}"/>'s
/// <remarks>This method is useful if you need to perform a side effect without altering the <see name="Task{T}"/>'s
/// value, such as logging.</remarks>
/// <typeparam name="T">The task's underlying type.</typeparam>
/// <param name="task">The task.</param>
/// <param name="onFulfilled">The function to execute if the task is fulfilled.</param>
/// <param name="onFaulted">The function to execute if the task is faulted.</param>
/// <returns>The task.</returns>
public static Task<T> Tap<T>(this Task<T> task, Action<T> onFulfilled, Func<Exception, Task> onFaulted)
=> task.IfFulfilled(onFulfilled).IfFaulted(onFaulted);
=> task.ContinueWith(continuationTask =>
{
if (continuationTask.IsCanceled)
{
return continuationTask;
}

return continuationTask.IsFaulted
? continuationTask.IfFaulted(onFaulted)
: continuationTask.IfFulfilled(onFulfilled);
}).Unwrap();

/// <summary>
/// Executes a function and discards the result on a <see name="Task{T}"/> whether it is in a fulfilled or faulted state.
/// </summary>
/// <remarks>This method is useful if you need to perform a side effect without altering the <see name"Task{T}"/>'s
/// <remarks>This method is useful if you need to perform a side effect without altering the <see name="Task{T}"/>'s
/// value, such as logging.</remarks>
/// <typeparam name="T">The task's underlying type.</typeparam>
/// <typeparam name="R">The output type of the <paramref name="onFaulted" /> function.</typeparam>
Expand All @@ -60,25 +84,45 @@ public static Task<T> Tap<T>(this Task<T> task, Action<T> onFulfilled, Func<Exce
/// <param name="onFaulted">The function to execute if the task is faulted.</param>
/// <returns>The task.</returns>
public static Task<T> Tap<T, R>(this Task<T> task, Func<T, Task<R>> onFulfilled, Action<Exception> onFaulted)
=> task.IfFulfilled(onFulfilled).IfFaulted(onFaulted);
=> task.ContinueWith(continuationTask =>
{
if (continuationTask.IsCanceled)
{
return continuationTask;
}

return continuationTask.IsFaulted
? continuationTask.IfFaulted(onFaulted)
: continuationTask.IfFulfilled(onFulfilled);
}).Unwrap();

/// <summary>
/// Executes a function and discards the result on a <see name="Task{T}"/> whether it is in a fulfilled or faulted state.
/// </summary>
/// <remarks>This method is useful if you need to perform a side effect without altering the <see name"Task{T}"/>'s
/// <remarks>This method is useful if you need to perform a side effect without altering the <see name="Task{T}"/>'s
/// value, such as logging.</remarks>
/// <typeparam name="T">The task's underlying type.</typeparam>
/// <param name="task">The task.</param>
/// <param name="onFulfilled">The function to execute if the task is fulfilled.</param>
/// <param name="onFaulted">The function to execute if the task is faulted.</param>
/// <returns>The task.</returns>
public static Task<T> Tap<T>(this Task<T> task, Func<T, Task> onFulfilled, Action<Exception> onFaulted)
=> task.IfFulfilled(onFulfilled).IfFaulted(onFaulted);
=> task.ContinueWith(continuationTask =>
{
if (continuationTask.IsCanceled)
{
return continuationTask;
}

return continuationTask.IsFaulted
? continuationTask.IfFaulted(onFaulted)
: continuationTask.IfFulfilled(onFulfilled);
}).Unwrap();

/// <summary>
/// Executes a function and discards the result on a <see name="Task{T}"/> whether it is in a fulfilled or faulted state.
/// </summary>
/// <remarks>This method is useful if you need to perform a side effect without altering the <see name"Task{T}"/>'s
/// <remarks>This method is useful if you need to perform a side effect without altering the <see name="Task{T}"/>'s
/// value, such as logging.</remarks>
/// <typeparam name="T">The task's underlying type.</typeparam>
/// <typeparam name="R">The output type of the <paramref name="onFaulted" /> function.</typeparam>
Expand All @@ -87,31 +131,58 @@ public static Task<T> Tap<T>(this Task<T> task, Func<T, Task> onFulfilled, Actio
/// <param name="onFaulted">The function to execute if the task is faulted.</param>
/// <returns>The task.</returns>
public static Task<T> Tap<T, R, S>(this Task<T> task, Func<T, Task<R>> onFulfilled, Func<Exception, Task<S>> onFaulted)
=> task.IfFulfilled(onFulfilled).IfFaulted(onFaulted);
=> task.ContinueWith(continuationTask =>
{
if (continuationTask.IsCanceled)
{
return continuationTask;
}

return continuationTask.IsFaulted
? continuationTask.IfFaulted(onFaulted)
: continuationTask.IfFulfilled(onFulfilled);
}).Unwrap();

/// <summary>
/// Executes a function and discards the result on a <see name="Task{T}"/> whether it is in a fulfilled or faulted state.
/// </summary>
/// <remarks>This method is useful if you need to perform a side effect without altering the <see name"Task{T}"/>'s
/// <remarks>This method is useful if you need to perform a side effect without altering the <see name="Task{T}"/>'s
/// value, such as logging.</remarks>
/// <typeparam name="T">The task's underlying type.</typeparam>
/// <param name="task">The task.</param>
/// <param name="onFulfilled">The function to execute if the task is fulfilled.</param>
/// <param name="onFaulted">The function to execute if the task is faulted.</param>
/// <returns>The task.</returns>
public static Task<T> Tap<T>(this Task<T> task, Func<T, Task> onFulfilled, Func<Exception, Task> onFaulted)
=> task.IfFulfilled(onFulfilled).IfFaulted(onFaulted);
=> task.ContinueWith(continuationTask =>
{
if (continuationTask.IsCanceled)
{
return continuationTask;
}

return continuationTask.IsFaulted
? continuationTask.IfFaulted(onFaulted)
: continuationTask.IfFulfilled(onFulfilled);
}).Unwrap();

/// <summary>
/// Executes a function and discards the result on a <see name="Task{T}"/> whether it is in a fulfilled or faulted state.
/// </summary>
/// <remarks>This method is useful if you need to perform a side effect without altering the <see name"Task{T}"/>'s
/// <remarks>This method is useful if you need to perform a side effect without altering the <see name="Task{T}"/>'s
/// value, such as logging.</remarks>
/// <typeparam name="T">The task's underlying type.</typeparam>
/// <param name="task">The task.</param>
/// <param name="onFulfilled">The function to execute if the task is fulfilled.</param>
/// <param name="onFaulted">The function to execute if the task is faulted.</param>
/// <returns>The task.</returns>
public static Task<T> Tap<T, R>(this Task<T> task, Func<T, Task> onFulfilled, Func<Exception, Task<R>> onFaulted)
=> task.IfFulfilled(onFulfilled).IfFaulted(onFaulted);
=> task.Tap(
value =>
{
onFulfilled(value);
return Task.FromResult(value);
},
onFaulted
);
}
Loading