JavaScriptでコールバック連結 (2)

こんにちは、株式会社CFlatです。
一週挟みましたが、「JavaScriptでコールバック連結」の続きです。
それでは前回の処理の流れを、もう一度見ていきます。

(1) ボタンを表示するためのアニメーションを実行する
(2) スロットアニメーションを読み込む
(3) (1)の完了後、ボタンが押されるのを待つ
(4) (3)が完了したら、サーバー上のAPIに、当たるか外れるかの結果を問い合わせる
(5) (3)が完了して(2)が未完了であれば、ローディング表示を行う
(6) (3)と(2)が完了したら、もしローディング表示があれば消し、スロットアニメーションを表示する
(7) (4)が完了したら、結果に応じたアニメーションを読み込む
(8) (6)が完了して(7)が未完了であれば、ローディング表示を行う
(9) (6)と(7)が完了したら、もしローディング表示があれば消し、結果アニメーションを表示する
(10) (9)が完了したら、特定のページに遷移する

これを一般化した書き方にすると、このようになります:

・非同期処理(1)を開始
・非同期処理(2)を開始
・非同期処理(1)の完了時、非同期処理(3)を開始
・非同期処理(3)の完了時、非同期処理(4)を開始
・非同期処理(3)が完了、かつ非同期処理(2)が未完了の際、処理(5)を実行
・非同期処理(2)(3)が共に完了時、非同期処理(6)を開始
・非同期処理(4)の完了時、非同期処理(7)を開始
・非同期処理(6)が完了、かつ非同期処理(7)が未完了の際、処理(8)を実行
・非同期処理(6)(7)が共に完了時、非同期処理(9)を開始
・非同期処理(9)の完了時、処理(10)を実行

どうも、ほとんどが同じことを示しているようですので、必要な機能を抽出してみます。

・ある処理を開始済みかどうか、終了済みかどうかを確認する
・0個以上の処理が全て完了するのを待って、次の処理を実行する
・処理のスケジューリングは、動的に行えなければならない

これらをコードに直すと、各非同期処理の失敗時の処理を除けば、こんな感じにするとよさそうです。

var scheduler = new Scheduler() ;
scheduler.wait([], {1: async_1, 2: async_2})
	.wait([1], {3:async_3})
	.wait([3], function(schedule) {
		if (!schedule.scheduler.isFinished(2)) schedule.scheduler.wait([], {5: func_5});
	})
	.wait([2, 3], function(schedule) {
		if (schedule.scheduler.isTriggered(5)) func_6a();
		schedule.scheduler.wait([], {6: async_6});
	})
	……

 上記の部分までを実際に組んでみたら、こうなりました。

(async3までの方が時間がかかる場合)
scheduling_test
async_1() started
async_2() started
async_1() finished
async_3() started
async_2() finished
async_3() finished
async_6() started
async_6() finished

(async2までの方が時間がかかる場合)
scheduling_test
async_1() started
async_2() started
async_1() finished
async_3() started
async_3() finished
func5() started
async_2() finished
func6a() started
async_6() started
async_6() finished

ソースはscheduler.jsscheduling.htmlです。