Skip to content

Commit f9c388f

Browse files
committed
Issue-110: Add TaskExtras.InvokeIf
closes #110 SemVer:Minor
1 parent a4387ac commit f9c388f

File tree

6 files changed

+307
-0
lines changed

6 files changed

+307
-0
lines changed

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,29 @@ Task.FromException<int>(new ArgumentException())
157157
))
158158
```
159159

160+
#### InvokeIf
161+
162+
`InvokeIf` can be used to conditionally invoke a function. This is most useful for side effects.
163+
164+
```c#
165+
Task.FromResult(someUrl)
166+
.Then(httpClient.GetAsync)
167+
.IfFulfilled(TaskExtras.InvokeIf(
168+
httpResponse => !httpResponse.IsSuccessStatusCode,
169+
httpResponse => _logger.LogWarning("Got '{StatusCode}' response from server", httpResponse.StatusCode)
170+
);
171+
```
172+
173+
However, it can be used with `.Then` as well:
174+
175+
```c#
176+
Task.FromResult(4)
177+
.Then(InvokeIf(
178+
value => value % 2 == 0,
179+
value => value + 1
180+
);
181+
```
182+
160183
[bluebird]: http://bluebirdjs.com/docs/getting-started.html
161184
[MONADS.md]: ./MONADS.md
162185
[nuget.org]: https://www.nuget.org/packages/RLC.TaskChaining/

src/TaskExtras.cs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,138 @@ Func<Exception, Task<T>> resolutionSupplier
131131
? resolutionSupplier(value)
132132
: Task.FromException<T>(value);
133133

134+
/// <summary>
135+
/// Invokes <paramref name="action"/> if <paramref name="predicate"/> succeeds.
136+
/// </summary>
137+
/// <example>This is useful for conditionally executing a side effect.
138+
/// <code>
139+
/// Task.FromResult(someUrl)
140+
/// .Then(httpClient.GetAsync)
141+
/// .IfFulfilled(InvokeIf(
142+
/// httpResponse => !httpResponse.IsSuccessStatusCode,
143+
/// response => _logger.LogWarning("Got '{StatusCode}' response from server", response.StatusCode)
144+
/// );
145+
/// </code>
146+
/// </example>
147+
/// <param name="predicate">A predicate to evaluate with the <see cref="Task{T}"/>'s value.</param>
148+
/// <param name="action">The function to invoke if <paramref name="predicate"/> returns <code>true</code>.</param>
149+
/// <typeparam name="T">The type passed into <code>InvokeIf</code>.</typeparam>
150+
/// <returns>A function that conditionally invokes another function.</returns>
151+
public static Func<T, Task<T>> InvokeIf<T>(
152+
Predicate<T> predicate,
153+
Action<T> action
154+
)
155+
{
156+
return value =>
157+
{
158+
if (predicate(value))
159+
{
160+
action(value);
161+
}
162+
163+
return Task.FromResult(value);
164+
};
165+
}
166+
167+
/// <summary>
168+
/// Invokes <paramref name="func"/> if <paramref name="predicate"/> succeeds.
169+
/// </summary>
170+
/// <example>This is useful for conditionally executing a side effect.
171+
/// <code>
172+
/// Task.FromResult(someUrl)
173+
/// .Then(httpClient.GetAsync)
174+
/// .IfFulfilled(InvokeIf(
175+
/// httpResponse => !httpResponse.IsSuccessStatusCode,
176+
/// response => _logger.LogWarning("Got '{StatusCode}' response from server", response.StatusCode)
177+
/// );
178+
/// </code>
179+
/// </example>
180+
/// <param name="predicate">A predicate to evaluate with the <see cref="Task{T}"/>'s value.</param>
181+
/// <param name="func">The function to invoke if <paramref name="predicate"/> returns <code>true</code>.</param>
182+
/// <typeparam name="T">The type passed into <code>InvokeIf</code>.</typeparam>
183+
/// <returns>A function that conditionally invokes another function.</returns>
184+
public static Func<T, Task<T>> InvokeIf<T>(
185+
Predicate<T> predicate,
186+
Func<T, T> func
187+
)
188+
{
189+
return value =>
190+
{
191+
if (predicate(value))
192+
{
193+
return Task.FromResult(func(value));
194+
}
195+
196+
return Task.FromResult(value);
197+
};
198+
}
199+
200+
/// <summary>
201+
/// Invokes <paramref name="func"/> if <paramref name="predicate"/> succeeds.
202+
/// </summary>
203+
/// <example>This is useful for conditionally executing a side effect.
204+
/// <code>
205+
/// Task.FromResult(someUrl)
206+
/// .Then(httpClient.GetAsync)
207+
/// .IfFulfilled(InvokeIf(
208+
/// httpResponse => !httpResponse.IsSuccessStatusCode,
209+
/// response => _logger.LogWarning("Got '{StatusCode}' response from server", response.StatusCode)
210+
/// );
211+
/// </code>
212+
/// </example>
213+
/// <param name="predicate">A predicate to evaluate with the <see cref="Task{T}"/>'s value.</param>
214+
/// <param name="func">The function to invoke if <paramref name="predicate"/> returns <code>true</code>.</param>
215+
/// <typeparam name="T">The type passed into <code>InvokeIf</code>.</typeparam>
216+
/// <returns>A function that conditionally invokes another function.</returns>
217+
public static Func<T, Task<T>> InvokeIf<T>(
218+
Predicate<T> predicate,
219+
Func<T, Task<T>> func
220+
)
221+
{
222+
return value =>
223+
{
224+
if (predicate(value))
225+
{
226+
return func(value);
227+
}
228+
229+
return Task.FromResult(value);
230+
};
231+
}
232+
233+
/// <summary>
234+
/// Invokes <paramref name="func"/> if <paramref name="predicate"/> succeeds.
235+
/// </summary>
236+
/// <example>This is useful for conditionally executing a side effect.
237+
/// <code>
238+
/// Task.FromResult(someUrl)
239+
/// .Then(httpClient.GetAsync)
240+
/// .IfFulfilled(InvokeIf(
241+
/// httpResponse => !httpResponse.IsSuccessStatusCode,
242+
/// response => _logger.LogWarning("Got '{StatusCode}' response from server", response.StatusCode)
243+
/// );
244+
/// </code>
245+
/// </example>
246+
/// <param name="predicate">A predicate to evaluate with the <see cref="Task{T}"/>'s value.</param>
247+
/// <param name="func">The function to invoke if <paramref name="predicate"/> returns <code>true</code>.</param>
248+
/// <typeparam name="T">The type passed into <code>InvokeIf</code>.</typeparam>
249+
/// <returns>A function that conditionally invokes another function.</returns>
250+
public static Func<T, Task<T>> InvokeIf<T>(
251+
Predicate<T> predicate,
252+
Func<T, Task> func
253+
)
254+
{
255+
return value =>
256+
{
257+
if (predicate(value))
258+
{
259+
return Task.FromResult(value).Then(func).Then(_ => value);
260+
}
261+
262+
return Task.FromResult(value);
263+
};
264+
}
265+
134266
/// <summary>
135267
/// A function that executes the <paramref name="supplier"/> after <paramref name="deferTime"/> has elapsed.
136268
/// </summary>

tests/unit/InvokeIf/WithAction.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using RLC.TaskChaining;
4+
using Xunit;
5+
6+
namespace RLC.TaskChainingTests.InvokeIf;
7+
8+
public class WithAction
9+
{
10+
[Fact]
11+
public async Task ItShouldInvokeIfPredicateSucceeds()
12+
{
13+
int actualValue = 0;
14+
int expectedValue = 5;
15+
Predicate<int> predicate = value => value % 2 == 0;
16+
Action<int> action = _ => { actualValue = 5; };
17+
18+
await Task.FromResult(4)
19+
.Then(TaskExtras.InvokeIf(predicate, action));
20+
21+
Assert.Equal(expectedValue, actualValue);
22+
}
23+
24+
[Fact]
25+
public async Task ItShouldNotInvokeIfPredicateFails()
26+
{
27+
int actualValue = 0;
28+
int expectedValue = 5;
29+
Predicate<int> predicate = value => value % 2 == 0;
30+
Action<int> action = _ => { actualValue = 5; };
31+
32+
await Task.FromResult(3)
33+
.Then(TaskExtras.InvokeIf(predicate, action));
34+
35+
Assert.NotEqual(expectedValue, actualValue);
36+
}
37+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using RLC.TaskChaining;
4+
using Xunit;
5+
6+
namespace RLC.TaskChainingTests.InvokeIf;
7+
8+
public class WithFullTaskFunc
9+
{
10+
[Fact]
11+
public async Task ItShouldInvokeIfPredicateSucceeds()
12+
{
13+
int expectedValue = 5;
14+
Predicate<int> predicate = value => value % 2 == 0;
15+
Func<int, Task<int>> func = _ => Task.FromResult(5);
16+
17+
int actualValue = await Task.FromResult(4)
18+
.Then(TaskExtras.InvokeIf(predicate, func));
19+
20+
Assert.Equal(expectedValue, actualValue);
21+
}
22+
23+
[Fact]
24+
public async Task ItShouldNotInvokeIfPredicateFails()
25+
{
26+
int expectedValue = 3;
27+
Predicate<int> predicate = value => value % 2 == 0;
28+
Func<int, Task<int>> func = _ => Task.FromResult(5);
29+
30+
int actualValue = await Task.FromResult(3)
31+
.Then(TaskExtras.InvokeIf(predicate, func));
32+
33+
Assert.Equal(expectedValue, actualValue);
34+
}
35+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using RLC.TaskChaining;
4+
using Xunit;
5+
6+
namespace RLC.TaskChainingTests.InvokeIf;
7+
8+
public class WithRawTaskFunc
9+
{
10+
[Fact]
11+
public async Task ItShouldInvokeIfPredicateSucceeds()
12+
{
13+
int actualValue = 0;
14+
int expectedValue = 5;
15+
Predicate<int> predicate = value => value % 2 == 0;
16+
Func<int, Task> func = value =>
17+
{
18+
actualValue = 5;
19+
return Task.CompletedTask;
20+
};
21+
22+
await Task.FromResult(4)
23+
.Then(TaskExtras.InvokeIf(predicate, func));
24+
25+
Assert.Equal(expectedValue, actualValue);
26+
}
27+
28+
[Fact]
29+
public async Task ItShouldNotInvokeIfPredicateFails()
30+
{
31+
int actualValue = 0;
32+
int expectedValue = 5;
33+
Predicate<int> predicate = value => value % 2 == 0;
34+
Func<int, Task> func = value =>
35+
{
36+
actualValue = 5;
37+
return Task.CompletedTask;
38+
};
39+
40+
await Task.FromResult(3)
41+
.Then(TaskExtras.InvokeIf(predicate, func));
42+
43+
Assert.NotEqual(expectedValue, actualValue);
44+
}
45+
}

tests/unit/InvokeIf/WithTFunc.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using RLC.TaskChaining;
4+
using Xunit;
5+
6+
namespace RLC.TaskChainingTests.InvokeIf;
7+
8+
public class WithTFunc
9+
{
10+
[Fact]
11+
public async Task ItShouldInvokeIfPredicateSucceeds()
12+
{
13+
int expectedValue = 5;
14+
Predicate<int> predicate = value => value % 2 == 0;
15+
Func<int, int> func = _ => 5;
16+
17+
int actualValue = await Task.FromResult(4)
18+
.Then(TaskExtras.InvokeIf(predicate, func));
19+
20+
Assert.Equal(expectedValue, actualValue);
21+
}
22+
23+
[Fact]
24+
public async Task ItShouldNotInvokeIfPredicateFails()
25+
{
26+
int expectedValue = 3;
27+
Predicate<int> predicate = value => value % 2 == 0;
28+
Func<int, int> func = _ => 5;
29+
30+
int actualValue = await Task.FromResult(3)
31+
.Then(TaskExtras.InvokeIf(predicate, func));
32+
33+
Assert.Equal(expectedValue, actualValue);
34+
}
35+
}

0 commit comments

Comments
 (0)