Skip to content

Instantly share code, notes, and snippets.

@kamranayub
Created January 27, 2013 16:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kamranayub/4649208 to your computer and use it in GitHub Desktop.
Save kamranayub/4649208 to your computer and use it in GitHub Desktop.
A Promise/A-like implementation of the `Then()` function. Accepts optional error callback, otherwise errors bubble up to next chained method. See: http://blogs.msdn.com/b/pfxteam/archive/2010/11/21/10094564.aspx
public static class TaskExtensions {
/// <summary>
/// A CommonJS Promise-like method that will execute a success and error callback, or just bubble up errors if not handled
/// </summary>
/// <typeparam name="TFirst"></typeparam>
/// <typeparam name="TNext"></typeparam>
/// <param name="first"></param>
/// <param name="success"></param>
/// <param name="error"></param>
/// <returns></returns>
public static Task<TNext> Then<TFirst, TNext>(this Task<TFirst> first, Func<TFirst, TNext> success, Func<AggregateException, TNext> error = null)
{
var tcs = new TaskCompletionSource<TNext>();
// First, execute first task and get it's result
first.ContinueWith(delegate {
if (first.IsFaulted && error == null)
{
tcs.TrySetException(first.Exception.InnerExceptions);
} else if (first.IsFaulted && error != null)
{
try
{
// If it errors out, we pass the error to the error callback which will do its own thing.
var t = Task.Factory.StartNew(() => error(first.Exception));
if (t == null) tcs.TrySetCanceled();
else
t.ContinueWith(delegate
{
if (t.IsFaulted) tcs.TrySetException(t.Exception.InnerExceptions);
else if (t.IsCanceled) tcs.TrySetCanceled();
else tcs.TrySetResult(t.Result);
}, TaskContinuationOptions.ExecuteSynchronously);
}
catch (Exception exc)
{
tcs.TrySetException(exc);
}
} else if (first.IsCanceled) tcs.TrySetCanceled();
else {
try {
var t = Task.Factory.StartNew(() => success(first.Result));
if (t == null) tcs.TrySetCanceled();
else t.ContinueWith(delegate {
if (t.IsFaulted) tcs.TrySetException(t.Exception.InnerExceptions);
else if (t.IsCanceled) tcs.TrySetCanceled();
else tcs.TrySetResult(t.Result);
}, TaskContinuationOptions.ExecuteSynchronously);
} catch (Exception exc) { tcs.TrySetException(exc); }
}
}, TaskContinuationOptions.ExecuteSynchronously);
return tcs.Task;
}
/// <summary>
/// A CommonJS Promise-like method that will execute a success and error callback, or just bubble up errors if not handled
/// </summary>
/// <typeparam name="TFirst"></typeparam>
/// <param name="first"></param>
/// <param name="success"></param>
/// <param name="error"></param>
/// <returns></returns>
public static Task<TFirst> Then<TFirst>(this Task<TFirst> first, Action<TFirst> success,
Action<AggregateException> error = null)
{
var s = success;
var e = error;
var r = first.Result;
Func<TFirst, TFirst> handleSuccess = x =>
{
s(x);
return x;
};
Func<AggregateException, TFirst> handleError = ex =>
{
if (e != null) e(ex);
return r;
};
return first.Then(handleSuccess, handleError);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment