不特定数のタスクをjQuery.Deferredで扱うTips

JavaScriptで非同期処理をかくとコールバック地獄になりやすい。
そんな非同期処理を可読性高く扱えるjQuery.Deferred。

しかし、1個だったり10個だったり不特定数の非同期タスクを
まとめてjQuery.Deferredで扱うのにハマったのでメモ。(順次ケースと並列ケース)
今回は例としてsetTimeout使っていますがajax処理やファイル読込みでの利用に便利だと思います。

不特定数の非同期を順次行う

非同期タスク
 ->A実行
  -> A完了
   -> B実行
    -> B完了
     -> C実行
      -> C完了
       -> 全部終わったら処理
ということがしたい場合の例です。

// タイマー
function delay(data){

  // 関数を返す
  return function(){

    var ret = new $.Deferred();
    console.log('start: ' + data + ' / ' + new Date().getTime());

    setTimeout(function(){
      console.log('end: ' + data + ' / ' + new Date().getTime());
      // OK通知
      ret.resolve();
    }, 3000);

    return ret.promise();
  }
}

// 実際の処理
(function(){
  var data = [1, 2, 3];

  var deferred = (new $.Deferred()).resolve();

  // タスクをつめる
  for(var i = 0; i < data.length; i++){
    deferred = deferred.then(delay(data[i]));
  }

  deferred.then(function(){
    // 全部終わったらログ出す
    console.log('tasks finish.' + new Date().getTime());
  });
})();
  • ポイントは非同期処理を順次行うために関数でくるむこと(上の例ではdelay())
  • for()文でdeferred.then()に順次つめていく



不特定数の並列処理を行う

非同期タスク
 -> A実行
    -> A完了
 -> B実行
   -> B完了
 -> C実行
     -> C完了
      -> 全部終わったら処理
ということがしたい場合の例です。

// タイマー
function delay(data){
  
  var ret = new $.Deferred();

  setTimeout(function(){
    console.log('timeout: ' + data + ' / ' + new Date().getTime());
    // OK通知
    ret.resolve();
  }, 3000);

  return ret.promise();
}

// 実際の処理
(function(){
  
  var data = [1, 2, 3];
  var tasks = [];

  // タスクをつめる
  for(var i = 0; i < data.length; i++){
    tasks.push(delay(data[i]));
  }

  var when = $.when.apply($, tasks);
  when.then(function(){
    // 全部終わったらログ出す
    console.log('tasks finish.' + new Date().getTime());
  });
})();
  • ポイントは、promiseしたDeferredをタスク配列につめること
  • $.when.apply($, tasks)でタスク配列を渡すこと

このようにjQuery.Deferredを使えば、複数の非同期処理を完結に表現できるのはいいですね。