jQuery.deferredを経由しつつES2015に入ったPromiseの雰囲気を味わう

Promiseを受け取るコードの練習が狙い

今回のコードは裏では動作の確認はしてるけど全体的には雰囲気を味わうものです。Promise がなんだかよく分からないので適当に sleep して返事を返すだけのサーバをでっち上げて、これまた普段使っていないのになぜか jQuery.deferred から試してみてます。既存の道具からのステップを小さくしておくことで理解しやすくなることを狙います。

また Promise そのものや Promsie を作る部分は注目してません。あくまで Promise を利用するのはそんなに難しくないな、という雰囲気を知るためのものです。

Node.js に詳しい人は 2015年リリースの v4 から使えるので、いちいち遠回りする必要はないと思いますし、すでに動いているコードを今回のように書き換える意味はないと思います。

jQuery.deferred

jQuery.Deferred() | jQuery API Documentation

まずは jQuery.deferred で実験。

実は jQuery はかなり古い 1.5 の時代から Deferred という機能を持っていて、ajax() や getJSON() などはこの Deferred オブジェクトを返しています。そしてこの Deferred オブジェクトが Promise とよく似た動作をします。ただし fail() は jQuery 用語で、ES2015 だと catch() になります。

1) 単純な成否

$.getJSON('/api1')
  .then(function() {
     // success
  })
  .fail(function() {
    // failure
  });

これだけだと if then else のように動く。

2) エラーをまとめて拾う

$.getJSON('/api1')
  .then(function() {
    return $.getJSON('/api2');
  })
  .fail(function() {
    // failure for api1 and api2
  });

then() の中で非同期処理を実行して「Promiseを返せば」どちらで失敗しても同じ fail() を通過する。

3) 異なるPromiseのをエラーを別々に拾う

$.getJSON('/api1')
  .then(function() {
    $.getJSON('/api2')
      .then(function() {
        // success
      })
      .fail(function() {
        // failure for api2
      });
    })
  .fail(function() {
    // failure for api1
  });

非同期ではあるけど if then の入れ子のような感じで書ける。中の fail() でエラーを拾っておけば外の fail() まで伝播することはない。

Promise ( ES2015 )

今度は jQuery のように使えて jQuery より小さいけど、Deferred を持っていない UmbrellaJS を使ったうえで、Promise の部分だけ ES2015 で補います。

※ IE 対応が必要な場合はトランスパイラや Polyfill が要ります。

まずはjQuery の $.getJSON() とほぼ同じ動きをして Promise を返す wrapper 関数 u.getJSON() を定義します。1ES2015 の Promise は jQuery.deferred と違って失敗時は catch() で拾います。

※ くりかえしますが、実際のプロダクトではこんなコードは必要ないです。

Umbrella JS

wrapper

u.getJSON = function(url) {
  return new Promise(function(resolve, reject) {
    u.ajax(url,
           {method: 'GET'},
           function(err, data) {
             if ( err ) {
               reject(err);
             } else {
               resolve(data);
             }
           }
          );
  });
}

1) 単純な成否

u.getJSON('/api1')
  .then(function(data) {
    // success
  })
  .catch(function(e) {
    // failure
    console.log(e + ' : api failed.');
  });

2) エラーをまとめて拾う

u.getJSON('/api1')
  .then(function() {
    // success
    return u.getJSON('/api2');
  })
  .catch(function() {
    // failure for api1 and api2
  });

3) 異なるPromiseのエラーを別々に拾う

u.getJSON('/api1')
  .then(function() {
    u.getJSON('/api2')
      .then(function() {
        // success
      })
      .catch(function() {
        // failure for api2
      });
    })
  .catch(function() {
    // failure for api1
  });

参考

応用編

  1. ちなみに u.ajax() は特に指定しなくても JSON string だったら自動的に parse してくれる謎機能が付いてます。 

More