LoginSignup
3
2

More than 5 years have passed since last update.

[C#]TaskクラスをJavaScriptのPromiseっぽく使ってみる実験

Last updated at Posted at 2016-03-29

C#の非同期でthen()とかcatch()とか書けたらどうなるの……という、あくまで実験です。
TaskSchedulerのことは何も考慮していません。
だからたぶん本番コードに投入しないほうがいいですよ。特にGUIなコードでは。

<追記>やはりこのコードではだめみたい。TaskSchedulerの問題ではなく、ContinueWithOnlyOnRanToCompletionを指定して得られたTaskに対して、さらにContinueWithNotOnRanToCompletionを指定したとしても、もともとのTaskの例外は拾えないようです。</追記>

最初に、使用例を示します。

Program.cs
using System;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        Foo()
            .Then(() =>
            {
                Console.WriteLine("Success!");
                return "succeeded";
            },
            ex =>
            {
                Console.WriteLine("Failure!");
                Console.WriteLine(ex == null ? "Canceled" : ex.ToString());
                return "failed";
            })
            .Then(str => Console.WriteLine(str))
            .Wait();
    }

    static async Task Foo()
    {
        await Task.Delay(1000);
        throw new TaskCanceledException("cancel test");
    }
}

次に、実装を示します。退屈なコードがただ並ぶだけですよ…

TaskEx.cs
using System;
using System.Threading.Tasks;

static class TaskEx
{
    #region T -> TResult
    public static Task<TResult> Then<T, TResult>(this Task<T> self, Func<T, TResult> onFulfilled)
    {
        return self
            .ContinueWith(t => onFulfilled(t.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
    }
    public static Task<TResult> Then<T, TResult>(this Task<T> self, Func<T, Task<TResult>> onFulfilled)
    {
        return self
            .ContinueWith(t => onFulfilled(t.Result), TaskContinuationOptions.OnlyOnRanToCompletion)
            .Unwrap();
    }
    public static Task<TResult> Then<T, TResult>(this Task<T> self, Func<T, TResult> onFulfilled, Func<AggregateException, TResult> onRejected)
    {
        return self
            .ContinueWith(t => onFulfilled(t.Result), TaskContinuationOptions.OnlyOnRanToCompletion)
            .ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion);
    }
    public static Task<TResult> Then<T, TResult>(this Task<T> self, Func<T, Task<TResult>> onFulfilled, Func<AggregateException, Task<TResult>> onRejected)
    {
        return self
            .ContinueWith(t => onFulfilled(t.Result), TaskContinuationOptions.OnlyOnRanToCompletion)
            .Unwrap()
            .ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion)
            .Unwrap();
    }
    public static Task<TResult> Catch<T, TResult>(this Task<T> self, Func<AggregateException, TResult> onRejected)
    {
        return self
            .ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion);
    }
    public static Task<TResult> Catch<T, TResult>(this Task<T> self, Func<AggregateException, Task<TResult>> onRejected)
    {
        return self
            .ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion)
            .Unwrap();
    }
    #endregion

    #region T -> void
    public static Task Then<T>(this Task<T> self, Action<T> onFulfilled)
    {
        return self
            .ContinueWith(t => onFulfilled(t.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
    }
    public static Task Then<T>(this Task<T> self, Func<T, Task> onFulfilled)
    {
        return self
            .ContinueWith(t => onFulfilled(t.Result), TaskContinuationOptions.OnlyOnRanToCompletion)
            .Unwrap();
    }
    public static Task Then<T>(this Task<T> self, Action<T> onFulfilled, Action<AggregateException> onRejected)
    {
        return self
            .ContinueWith(t => onFulfilled(t.Result), TaskContinuationOptions.OnlyOnRanToCompletion)
            .ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion);
    }
    public static Task Then<T>(this Task<T> self, Func<T, Task> onFulfilled, Func<AggregateException, Task> onRejected)
    {
        return self
            .ContinueWith(t => onFulfilled(t.Result), TaskContinuationOptions.OnlyOnRanToCompletion)
            .Unwrap()
            .ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion)
            .Unwrap();
    }
    public static Task Catch<T>(this Task<T> self, Action<AggregateException> onRejected)
    {
        return self.ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion);
    }
    public static Task Catch<T>(this Task<T> self, Func<AggregateException, Task> onRejected)
    {
        return self
            .ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion)
            .Unwrap();
    }
    #endregion

    #region void -> T
    public static Task<TResult> Then<TResult>(this Task self, Func<TResult> onFulfilled)
    {
        return self
            .ContinueWith(t => onFulfilled(), TaskContinuationOptions.OnlyOnRanToCompletion);
    }
    public static Task<TResult> Then<TResult>(this Task self, Func<Task<TResult>> onFulfilled)
    {
        return self
            .ContinueWith(t => onFulfilled(), TaskContinuationOptions.OnlyOnRanToCompletion)
            .Unwrap();
    }
    public static Task<TResult> Then<TResult>(this Task self, Func<TResult> onFulfilled, Func<AggregateException, TResult> onRejected)
    {
        return self
            .ContinueWith(t => onFulfilled(), TaskContinuationOptions.OnlyOnRanToCompletion)
            .ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion);
    }
    public static Task<TResult> Then<TResult>(this Task self, Func<Task<TResult>> onFulfilled, Func<AggregateException, Task<TResult>> onRejected)
    {
        return self
            .ContinueWith(t => onFulfilled(), TaskContinuationOptions.OnlyOnRanToCompletion)
            .Unwrap()
            .ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion)
            .Unwrap();
    }
    public static Task<TResult> Catch<TResult>(this Task self, Func<AggregateException, TResult> onRejected)
    {
        return self
            .ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion);
    }
    public static Task<TResult> Catch<TResult>(this Task self, Func<AggregateException, Task<TResult>> onRejected)
    {
        return self
            .ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion)
            .Unwrap();
    }
    #endregion

    #region void -> void
    public static Task Then(this Task self, Action onFulfilled)
    {
        return self
            .ContinueWith(t => onFulfilled(), TaskContinuationOptions.OnlyOnRanToCompletion);
    }
    public static Task Then(this Task self, Func<Task> onFulfilled)
    {
        return self
            .ContinueWith(t => onFulfilled(), TaskContinuationOptions.OnlyOnRanToCompletion)
            .Unwrap();
    }
    public static Task Then(this Task self, Action onFulfilled, Action<AggregateException> onRejected)
    {
        return self
            .ContinueWith(t => onFulfilled(), TaskContinuationOptions.OnlyOnRanToCompletion)
            .ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion);
    }
    public static Task Then(this Task self, Func<Task> onFulfilled, Func<AggregateException, Task> onRejected)
    {
        return self
            .ContinueWith(t => onFulfilled(), TaskContinuationOptions.OnlyOnRanToCompletion)
            .Unwrap()
            .ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion)
            .Unwrap();
    }
    public static Task Catch(this Task self, Action<AggregateException> onRejected)
    {
        return self
            .ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion);
    }
    public static Task Catch(this Task self, Func<AggregateException, Task> onRejected)
    {
        return self
            .ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion)
            .Unwrap();
    }
    #endregion
}
3
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2