[JavaScript] 連続する非同期処理をすっきり書いてコールバック地獄を回避する『MyAsync.js』

**　JavaScript では、実行結果を非同期で受け取る処理（ネットワーク越しの処理など）が多いと、コールバックが多発します。そこで、すっきり書くためのライブラリ「MyAsync.js」を自作しました。**

**　jQuery.Deferred や Promise などより手軽に使えます。使い方は [async.js](https://github.com/caolan/async/) と似てますが、ソースはもっとコンパクトなので、自由に改変して使って下さい。**

# ![](//goo.gl/tuSQrq) まずは MyAsync の使い方

## ■順次実行する MyAsync.<font color="red">run( )</font>

複数の非同期処理を順番に実行します。
`MyAsync.run(処理１,処理２,処理３,‥‥).end(最終処理);`

```html:HTML/JavaScript
<script src="MyAsync.js"></script>
<script>
MyAsync.run(
	function(baton) {
		// 処理１
		proc_1(function(result) {
			baton.next(result); // 次の処理へ
		});
	},
	function(baton) {
		// 処理１の結果
		var result_1 = baton.result;
		// 処理２
		proc_2(function(result) {
			baton.next(result); // 次の処理へ
		});
	},
	function(baton) {
		// 処理２の結果
		var result_2 = baton.result;
		// 処理３
		proc_3(function(result) {
			baton.next(result); // 次の処理へ → end()
		});
	}
).end(
	function(baton) {
		// 処理３の結果
		var result_3 = baton.result;
		// 最終処理などを記述
		......
	}
);
</script>
```

## ■一斉実行する MyAsync.<font color="red">all( )</font>

複数の非同期処理を一斉に実行します。
`MyAsync.all(処理１,処理２,処理３,‥‥).end(最終処理);`

```html:HTML/JavaScript
<script src="MyAsync.js"></script>
<script>
MyAsync.all(
	function(baton) {
		// 処理１
		proc_1(function(result) {
			baton.next(result); // 処理結果を登録
		});
	},
	function(baton) {
		// 処理２
		proc_2(function(result) {
			baton.next(result); // 処理結果を登録
		});
	},
	function(baton) {
		// 処理３
		proc_3(function(result) {
			baton.next(result); // 処理結果を登録
		});
	}
).end(
	function(baton){
		// 処理１～３の結果
		var result_1 = baton.results[0];
		var result_2 = baton.results[1];
		var result_3 = baton.results[2];
		// 最終処理などを記述
		......
	}
);
</script>
```


# ![](//goo.gl/tuSQrq) ざっくり解説

## 順次実行 MyAsync.<font color="red">run( )</font> について

- `run(...)` の引数に記述した順番で１つずつ実行されます
- 各々の非同期処理を `function(baton){...}` でくるんで下さい
- `baton.next()` で、各処理の完了を MyAsync に通知します
- `baton.next(result)` で、処理結果を次の処理へ渡すこともできます
- `baton.next(result, ms)` で、次の処理を ms ミリ秒遅延実行できます
- `baton.result` で、直前の処理結果を取得できます
- `baton.stop(result)` で、処理の中断もできます（`end()`は実行されます）

## 一斉実行 MyAsync.<font color="red">all( )</font> について

- `all(...)` の引数に記述した処理を一斉に実行しますが、終了は順不同です
- 各々の非同期処理を `function(baton){...}` でくるんで下さい
- `baton.next(result)` で、各処理の完了を MyAsync に通知します
- end処理では `baton.results` 配列に全ての処理結果が格納されています
- 終了順に関係なく `baton.results` 配列には処理１の結果から格納されます

# ![](//goo.gl/tuSQrq) MyAsync.js

```javascript:MyAsync.js
//------------------------------------------------
// MyAsyncモジュール
//------------------------------------------------
var MyAsync = (function() {
	var self = {};
	
	// 順次実行
	self.run = function() 
	{
		var run_funcs = arguments;
		var run_index = -1;
		var end_func  = null;
		var baton = {
			index  : -1,
			result : "",
			next   : function(result, wait) {
				if(result === undefined) result = "";
				if(wait   === undefined) wait = 0;
				run_index++;
				baton.index  = run_index;
				baton.result = result;
				setTimeout(function(){
					if(run_index < run_funcs.length) {
						run_funcs[run_index](baton);
					} else if(end_func) {
						end_func(baton);
					}
				}, wait);
			},
			stop  : function(result) {
				if(end_func) {
					baton.index = run_funcs.length;
					baton.result = result;
					baton.stopped = true;
					baton.stop_index = run_index;
					end_func(baton);
				}
			},
			stopped    : false,
			stop_index : -1,
		};
		
		baton.next();
		
		return {
			end : function(callback){
				end_func = callback;
			}
		};
	};
	
	// 一斉実行
	self.all = function() 
	{
		var all_funcs   = arguments;
		var all_results = [];
		var cnt_done = 0;
		var cnt_loop = 0;
		var end_func = null;
		
		(function loop(){
			if(cnt_loop >= all_funcs.length) 
				return;
			var baton = {
				index : cnt_loop,
				next  : function(result) {
					if(result === undefined) result = "";
					all_results[this.index] = result;
					cnt_done++;
					// 全処理が終了
					if(cnt_done == all_funcs.length){
						if(end_func) {
							baton.index   = all_funcs.length;
							baton.results = all_results;
							end_func(baton);
						}
					}
				},
				stop  : function(result) {
					baton.next(result);
				}
			};
			all_funcs[cnt_loop](baton);
			cnt_loop++;
			setTimeout(loop, 0);
		})();
		
		return {
			end : function(callback){
				end_func = callback;
			}
		};
	};
	
	return self;
})();
```

# ![](//goo.gl/tuSQrq) 参考記事

・[JavaScriptの非同期処理について](https://qiita.com/yasuno0327/items/df764997ef6d54805f21)
・[JavaScriptの同期、非同期、コールバック、プロミス辺りを整理してみる](https://qiita.com/YoshikiNakamura/items/732ded26c85a7f771a27)
・[非同期処理ってどういうこと？JavaScriptで一から学ぶ](https://qiita.com/kiyodori/items/da434d169755cbb20447)
・[JavaScriptのasync.jsでwaterfallとseries、parallelの違い](https://qiita.com/takeharu/items/84ffbee23b8edcbb2e21)

(･o･ゞ いじょー。