LoginSignup
4
4

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-02-09

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

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

まずは MyAsync の使い方

■順次実行する MyAsync.run( )

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

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

■一斉実行する MyAsync.all( )

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

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

ざっくり解説

順次実行 MyAsync.run( ) について

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

一斉実行 MyAsync.all( ) について

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

MyAsync.js

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;
})();

参考記事

JavaScriptの非同期処理について
JavaScriptの同期、非同期、コールバック、プロミス辺りを整理してみる
非同期処理ってどういうこと?JavaScriptで一から学ぶ
JavaScriptのasync.jsでwaterfallとseries、parallelの違い

(・o・ゞ いじょー。

4
4
0

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
4
4