（多分）東邦随一のIx Async歩き回り

#そもそもなんなのか？
Microsoftが提供しているIxファミリの１つ。
個人的な見解では、Enumerable/Enumeratorの対を非同期化して別の何かにしてみた感じ。

#前準備
Nugetにて、*”Ix-Async”*を参照すれば使えます。

#使い方
##事前の想定
System.Int32を引数にとって、System.Int32を返す結構重めの処理があったと想定して、以下のメソッドを用意しています。

```csharp

private static int SomeHeavyProcess(int value)
{
	//重い処理の変わりにてけとーに遅延を挟む。(500ms)
	Task.Delay(500).Wait();
	//適当に返す
	return value*2;
}

```

##前座：同期的処理でやってみる
上記メソッドをたとえば、0~10の値を適用した上で、その合計と平均を出力する場合、おおむね以下の通りとなる。

```csharp

static void SyncProcess()
{
	var sum = Enumerable.Range(0, 10).Select(SomeHeavyProcess).Sum();
	Console.WriteLine("Summation is " + sum);
	Console.WriteLine("Average is " + (sum/10.0));
}

```

ただ、GUIを使ってこんな書き方したら、5秒間もUIスレッドをブロックする残念実装になる。
また、CUIにおいても、たとえばAAを使った、プログレスやら、実行中のアニメを挟めないのでやっぱり残念実装になる。

##二ツ目：とりま書いてみる
前座の処理をIx Asyncを使って書いてみると、おおむね以下の通り。

```csharp

private static void UseIsAsync()
{
	//Enumerable.Rangeぢゃなくて、AsyncEnumerable.Rangeを使う。
	var parameters = AsyncEnumerable.Range(0, 10);

	//Taskにくるまって結果が戻ってくる。
	var sum = parameters.Select(SomeHeavyProcess).Sum();

	//CUIなので、お気楽に待つ（ポーリングする）
	for (;;)
	{
		Console.SetCursorPosition(0, 0);
		Console.Write('-');
		Task.Delay(100).Wait();
		Console.SetCursorPosition(0, 0);
		Console.Write('/');
		Task.Delay(100).Wait();
		Console.SetCursorPosition(0, 0);
		Console.Write('|');
		Task.Delay(100).Wait();
		Console.SetCursorPosition(0, 0);
		Console.Write('\\');
		Task.Delay(100).Wait();
		Console.SetCursorPosition(0, 0);
		Console.Write('-');
		Task.Delay(100).Wait();


		//あんまり良くないポーリングの実例（ポージングそのものが実はあんまり良くないけど）
		if(sum.Status== TaskStatus.RanToCompletion) break;
	}

	Console.Clear();
	Console.WriteLine("Summation is " + sum.Result);
	Console.WriteLine("Average is " + (sum.Result/10.0));

}


```

Sumメソッドの適用結果がTask<int>になってやってくるのがミソ。
コレなら待機可能でしょっ？て感じ。

##くいつき：async/awaitとともに使う
先の例だと、Sumの結果がTask<T>に詰まってやってきてる。コレは当然のことながらawaitableなので、async/awaitの中で以下のように利用できちゃったりする。

```csharp

private static async Task<Tuple<int,double>> UseAsyncEnumerable()
{
	var parameters = AsyncEnumerable.Range(0, 10);
	var sum = await parameters.Sum();
	return Tuple.Create(sum, (sum/10.0));
}
```
実際、私感だけど、こいつらのレゾンテートルはまさにここいらへんにあるんじゃ無いかなって思ってる。[^4]
[^4]: mappingを非同期実行できるというかそんなかんじ？


##ひざがわり：結局こいつら何なの？
ざっと使ってみたので、少し追っかけてみましょってことで。。。
AsyncEnumerable.Rangeの返す代物がなんなのか見てみると、IAsyncEnumerable<int>が返ってくる。これは、Enumerable.RangeがIEnumerable<int>を返し、Observable.Rangeが、IObservable<int>を返すのととってもとっても似てる。
じゃあ、IasyncEnumerable<T>ってなんなのさ？っていうと、IEnumerable<T>ととてもよく似てるけど決定的に違う何か。よく似ている理由は、GetEnumeratorメソッドを持っていること。決定的に違うのは当該メソッドの戻り値がIEnumerator<T>じゃなくて、IAsyncEnumerator<T>になっている点。
んだばIAsyncEnumerator<T>って何なのさ？っていうと、コレもこれで、IEnumerator<T>ととってもよく似てるけど決定的に違う何か。よく似ている理由は、

 * IDisposableだよ！
 * MoveNextメソッドを持ってるよ！
 * Currentプロパティも完備！
 * MoveNext読んでからじゃないとCurrentプロパティ読んでも意味ないよ！

じゃあ何が違うかというと

 * 時代の遺物たるResetメソッドはないよ [^1]　
 * MoveNextの戻り値はboolじゃなくて、Task<bool>だよ！
 * 待機完了後にCurrent読まないとダメだよ
 * foreachで回せないよ！ [^2]

[^1]: こいつは元々COMとの兼ね合いだったし、yield使うと例外が飛んでくる程度にいらない子
[^2]: 先の通りMoveNext()がbool返してくれないから使えない。

という感じ。
foreachで回せないのはぱっと見激痛だけど、実際は拡張メソッドにForeEachメソッドがあるので、そこまで実際問題にはならなかったりする。ただ、普通に下記のように書いた場合、同期的な実行と同じになっちゃうので、その点は注意。

```csharp
		private static void Main(string[] args)
		{
			//同期的実行になってあんまりおいしくない。
			AsyncEnumerable.Range(0, 10).Select(SomeHeavyProcess).ForEach(Console.WriteLine);
		}

```

それじゃ、ってことでForEachもLinqもなしに、合計を出したい場合、どーするか？と言うと、こんな感じ。
コレを見ていただければ何がどーなってるのか、あらかたご理解いただけるかと。。。

```csharp

private static void Main(string[] args)
{
	var result = AsyncEnumerable.Range(0, 10).Select(SomeHeavyProcess);
	var accum = 0;

	using (var iterator = result.GetEnumerator())
	{
		for (;;)
		{
			//Task<bool>貰ってくる。
			var cond = iterator.MoveNext();
			//しばし待つ！
			cond.Wait();

			if (cond.Result) accum += iterator.Current;
			else break;
		}
	}


	Console.WriteLine("Summation is " + accum);
	Console.WriteLine("Average is " + (accum/10.0));
}

```

注意するべきは、非同期であって並行ではないという点。
MoveNextをガンガン回して斉時的に結果を得ようとしても結果は酷いことになる。
例えば下記みたいなコトはダメ、絶対

```csharp

		private static void Main(string[] args)
		{
			var seq = AsyncEnumerable.Range(0, 10).Select(x =>
			{
				var ret = SomeHeavyProcess(x);
				//行儀が悪いけど、どー鳴ってるか内部状態を書き出す。
				Console.WriteLine(ret);
				return (object) null;
			});

			var iterator = seq.GetEnumerator();

			for (var i = 0; i < 10; i++)
			{
				//とりあえず待機無しで10回ぶん回してみる。
				iterator.MoveNext();
			}


			Console.ReadLine();

			//結果は惨憺たる状態
			//10
			//18
			//4
			//16
			//10
			//10
			//12
			//4
			//12
			//14
		}

```

##トリ：まとめ
非同期にIEnumerable<T>/IEnumerator<T>、そしてLINQを構築すると、どんな世界になる？
てコトの解の一つじゃないかなと言うのが実感。
ただ、なまじ、IEnumerable<T>/IEnumerator<T>と酷似しすぎているのに何か違っていて結構混乱してしまうって点と、根本論としてこの手扱うならRxの方がベストじゃね？っていうオチ。んでもって、とどめに、async/awaitを使うと、IEnumerable<T>/IEnumerator<T>の世界でも普通に非同期化できてしまうので、そー言う面においては、わりかし微妙な立ち位置になってるかな？と、個人的に思ったり思わなかったりしますです。えぇ。

###参考文献
 * [非同期時代のLINQ](http://neue.cc/2013/12/04_435.html)