コンテンツへスキップ

hifiveのPromise(Deferred)を使って非同期処理を順番に処理しよう

by : 2016/02/22

JavaScriptは非同期処理が得意な言語です。たとえば以下のコードは1秒後にHello Worldを出力します。

setTimeout(function () {
  console.log("Hello World");
}, 1000)

また、Ajaxも非同期処理になるので、かつてはコールバック関数を使って処理を書いていました。

$.ajax({
  /* 処理 */
  success: function () {
    // ネットワーク処理成功時
  },
  error: function () {
    // ネットワーク処理失敗時
  }
});

しかしこの書き方では2回目のネットワーク処理、3回目のネットワーク処理…と続けていこうとした際にどんどんネストが深くなってしまう問題がありました。そこで使われるようになっているのがPromiseになります。

Promiseでは非同期処理をまとめて行えるようになっており、各非同期処理がPromiseオブジェクトを返却してつなげていくことができます。

var func1 = function () {
  // 非同期処理
}.then(function () {
  // 次の非同期処理
});

そして複数の非同期処理をまとめる際にはwhenを使います。

Promise.when(function () {
  /* 非同期処理 */
}, function () {
  /* 非同期処理 */
}).then(function () {
  // 処理完了
});

ただしこの場合、各非同期処理の実行順番については保証されません。例えば次のようなコードがあったとします。

for (var i = 0; i <= 5; i++) {
  var test = function (count) {
    var randnum = Math.floor( Math.random() * 100 );
    setTimeout(function () {
      console.log(count);
    }, randnum * 100);
  };
  test(i);
}

これを実行すると、

5
3
1
0
2
4

のように表示されたりします(実行時によって結果が変わります)。これを正しく0〜5といった感じに並ぶようにするにはどうしたら良いでしょうか。

Promise.allを試してみる

一部のブラウザではPromiseが使えるようになっています。これのPromise.allを使ってみます。

ary = [];
for (var i = 0; i <= 5; i++) {
  var promise = new Promise(function(resolve, reject){
    var test = function (count) {
      var randnum = Math.floor( Math.random() * 100 );
      setTimeout(function () {
        console.log(count);
        resolve(count);
      }, randnum * 100);
    };
    test(i);
  });
  ary.push(promise);
}

Promise.all(ary).then(function (results) {
  console.log(results);
});

この方法の場合、Promise.allの結果としては [0, 1, 2, 3, 4, 5] といった形で正しく入ってきますが、その前のログは

1
3
5
4
2
0

といった形で実行順番は保証されていません。

hifiveのh5.async.loopを使う

そこで使ってみて欲しいのがh5.async.loopです。h5.async.loopを使うと実行順番を保証したPromiseでのループ処理が書けます。

var loop = h5.async.loop(new Array(5), function (i, v) {
  var deffered = h5.async.deferred();
  var randnum = Math.floor( Math.random() * 100 );
  var test = function (count) {
    setTimeout(function () {
      console.log(count);
      deffered.resolve(count);
    }, randnum * 100);
  };
  test(i);
  return deffered.promise();
});

loop.done(function () {
  console.log("処理完了");
});

のように書きます。h5.async.loopの最初の引数は配列になります。例えば [‘a’, ‘b’, ‘c’, ‘d’, ‘e’] のような配列を渡せば、iにはインデックス(0〜)が、vには値(aまたはbなど)が順番に入ります。

そしてまず最初にdeferredを生成します。そして最後にdeferred.promise()を返します。後はその中で非同期処理を実行し、deferred.resolve(成功時)、deferred.reject(失敗時)を呼び出すだけです。

そして h5.async.loop の done メソッドには各Promiseでのresolve時に渡した引数が入ってきます。これだけで実行順番も保証された非同期のループ処理が書けます。

メリット

Ajax処理においては同期、非同期の選択ができます。そのためいざとなれば同期処理にすることで実行順番の保証ができます。しかしsetTimeoutのような処理はできません。h5.async.loopを使えばどんな非同期処理においても使えますので便利です。

また、実行順番が保証されますので安心して処理が実行できます。ただし前の関数の実行結果を使いたいといったような処理を書こうと思うと面倒になりますので、その場合はPromiseオブジェクトをthenメソッドでつないでくのが良いでしょう。


hifiveのPromise(Deferred)を使えばコールバック地獄から解放され、見通しの良いコードが書けるようになります。さらに実行順番を保証したループ処理も簡単です。詳細は非同期処理とPromise(Deferred)を背景から理解しよう – hifiveをご覧ください。ぜひ使ってみてください!

From → hifive

コメントは受け付けていません。

%d人のブロガーが「いいね」をつけました。