2015年5月18日月曜日

[JS標準] Promiseでコールバック地獄を解消しよう

JavaScriptのES6で Promise が標準装備されました。これはPromiseパターンを実現するためのAPIです。Promiseのメソッドチェーン(then)を使うことで、JSコードに有りがちなコールバック地獄から解放されるという大きなメリットがあります。
以下は Promise を使った簡単なサンプルです。

以下のサンプルでは

(1)jsonを返すWebサーバにGET
(2)GETしたレスポンスをJSONに変換
(3)JSONからプロパティ "message" を取り出す

という処理をあえてPromiseのメソッドチェーン (then) を使って実現しています。

では、まずはGET対象のjsonファイルを作っておきましょう。

test.json
{
  "message": "Hello World!"
}

次はjsの中身です。

example.html
<script>
function get(url) {
  // Return a new promise.
  return new Promise(function(resolve, reject) {
    // Do the usual XHR stuff
    var req = new XMLHttpRequest();
    req.open('GET', url);

    req.onload = function() {
      // This is called even on 404 etc
      // so check the status
      if (req.status == 200) {
        // Resolve the promise with the response text
        return resolve(req.response);
      }
      else {
        // Otherwise reject with the status text
        // which will hopefully be a meaningful error
        return reject(Error(req.statusText));
      }
    };

    // Handle network errors
    req.onerror = function() {
      return reject(Error("Network Error"));
    };

    // Make the request
    req.send();
  });
}

var url = 'http://localhost:8000/test.json';

get(url).then(function(response) {
  console.log("response:", response);
  return JSON.parse(response);
}).then(function(json) {
  console.log("json:", json);
  return json.message;
}).then(function(message) {
  console.log("message:", message);
  return message;
}).catch(function(err){
  console.log("err:", err);
});

</script>

重要なのは #get() の中に出てくる resolve と reject です。
resolve(req.response) で、 then() で呼ばれる次の関数に req.response を引数として渡しています。そして reject(Error(...)) では、catch() で呼ばれる関数にErrorオブジェクトを渡しています。
#get() が Promise オブジェクトをreturnしている点も重要です。

これにより、

get(url)
.then(function(obj){...})
.then(function(obj){...})
.then(function(obj){...})
...
.catch(function(err){...}) 

といったような記述が可能となります。
then()の中の関数でreturnされた値は、次のthen()の関数に引数として受け継がれます。

起動&実行

$ python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000 ...

http://localhost:8000/example.html を叩くと、ブラウザのコンソールに以下のようにログ出力されます。

response: {
  "message": "Hello World!"
}
json: Object {message: "Hello World!"}
message: Hello World!

サンプルは以上となります。

Promiseを上手く使いこなせばデバッグも捗りそうですね。
Promiseパターン(future, promise, delay)は以前から存在していましたが、JavaScriptで標準化されたというのは非常に大きな一歩だと思います。これでPromiseパターンの普及にも弾みがつくのではないでしょうか。

■参考にさせていただいたサイト
JavaScript Promises: There and back again - HTML5 Rocks

0 件のコメント:

コメントを投稿