patterncsharpMinor
Implementing JQuery style 'deferred' and 'promise' in C#
Viewed 0 times
deferredpromisestylejqueryandimplementing
Problem
I like the pattern of the jQuery Deferred object.
I like how you can call
I couldn't find an equivalent in the C# library, so here is my implementation (a basic subset so far):
For this code to be usable, it has to be completely 100% thread safe. Can anyone see a hole in it?
Also, am I just re-implementing something that already exists in some corner of the .NET base class library?
For the sake of context I'll provide a practical use: there's a long running application, and some code that must execute if a file exists. The file might exist when the code first runs, or might come to exist later, or might never exist at all.
I invoke it like this:
```
string fullPath = GenerateWatchedFilePath();
FileSystemWatcherUtils
.WhenFileAppears(fullPath)
.Done(() =>
{
using (var stream = File.OpenRead(fullPath))
I like how you can call
Resolve any number of times, but the listening objects will only be notified once. I also like how you can attach a listening object after Resolve has already been called, and the listening object will still get the notification. If you're programming in an uncertain multithreaded environment, it greatly reduces the amount of thinking you have to do.I couldn't find an equivalent in the C# library, so here is my implementation (a basic subset so far):
using System;
using System.Collections.Concurrent;
using System.Threading;
interface IPromise
{
void Done(Action action);
}
sealed class Deferred : IPromise
{
readonly ConcurrentBag _actions = new ConcurrentBag();
// using an int instead of a bool
// so we can use Interlocked
private int _isResolved = 0;
internal void Resolve()
{
Interlocked.Exchange(ref _isResolved, 1);
InvokeActions();
}
void IPromise.Done(Action action)
{
_actions.Add(action);
if (_isResolved == 1)
{
InvokeActions();
}
}
void InvokeActions()
{
Action action = null;
while (_actions.TryTake(out action))
{
action();
}
}
}For this code to be usable, it has to be completely 100% thread safe. Can anyone see a hole in it?
Also, am I just re-implementing something that already exists in some corner of the .NET base class library?
For the sake of context I'll provide a practical use: there's a long running application, and some code that must execute if a file exists. The file might exist when the code first runs, or might come to exist later, or might never exist at all.
I invoke it like this:
```
string fullPath = GenerateWatchedFilePath();
FileSystemWatcherUtils
.WhenFileAppears(fullPath)
.Done(() =>
{
using (var stream = File.OpenRead(fullPath))
Solution
I like how you can call
This sounds a lot like
Assuming you have a
The difference with your code is that
Resolve any number of times, but the listening objects will only be notified once. I also like how you can attach a listening object after Resolve has already been called, and the listening object will still get the notification. If you're programming in an uncertain multithreaded environment, it greatly reduces the amount of thinking you have to do.This sounds a lot like
TaskCompletionSource along with the Task it controls.Assuming you have a
TaskCompletionSource tcs:- To attach a listener, use
tcs.Task.ContinueWith(_ => / listener code here /). You can do this before or after theTaskhas been completed and the listener code will be executed only once after theTaskhas been completed.
- To complete the
Task(the equivalent of resolving), usetcs.TrySetResult(someValue). If you'll use theTrySetResult()variant instead ofSetResult(), the second and following attempt at completing will be ignored (SetResult()would throw an exception).
The difference with your code is that
TaskCompletionSource always has some result type. You can either just ignore that (using e.g. TaskCompletionSource and SetResult(null)), or use the non-generic TaskCompletionSource from AsyncEx.Context
StackExchange Code Review Q#70965, answer score: 4
Revisions (0)
No revisions yet.