はじめに
今回は、Ajax、Deferred機能などを中心に紹介しましょう。
また、先日7月7日に、jQuery 3.1.0がリリースされましたので、そちらの変更点も併せてお伝えします。
対象読者
- Webアプリケーション開発に興味があり、jQueryを知っている方
Deferred機能
$.Deferredは、jQuery 1.5から導入された非同期処理のための標準モジュールです。ここでは、少し$.Deferredの基本的なところをおさらいしてから、jQuery 3での変更点を解説することにしましょう。
なお、jQuery 3での$.Deferredでは、破壊的な変更が3つあり、そのうち2つには新機能が含まれています。
$.Deferredとは
Deferredとは、英語で、遅延、据え置きされた、などの意味 で、$.Deferredは、Promisesという概念を元に設計されたオブジェクトです。Promises(約束)の概念は、身近な例でいうと、フードコードなどで使われる呼び出しベルのようなものです。非同期処理を待つ(ブロッキング)代わりに、将来のある時点の応答を返すオブジェクト(Promises)を使ってノンブロッキングに処理を行う、という考え方です。$.Deferredでは、このPromiseオブジェクトを内包しています。
Promiseオブジェクトは、初期状態ではpendingという状態になっています。通常、非同期処理を実行後に、resolveメソッドかrejectメソッドで、Deferrdの状態を確定させます。resolveメソッドは、正常終了、rejectメソッドは異常終了した状態を表します。正常終了時には、doneメソッドで設定したコールバック、異常終了時には、failメソッドで設定したコールバックが実行されます。
また、thenメソッドを使えば、done、failメソッドを同時に定義できる上、新たなDeferrdオブジェクトを返します。したがってメソッドチェーンが使え、非同期処理を順番に処理したい場合でも、コールバックを多重に定義することなく、スマートに記述することができます。
Promises/A+互換
最初の変更点は、$.Deferredが非同期処理の標準仕様であるPromises/A+に準拠したことです。jQueryの$.Deferredでは、Promises/A+の仕様と一部異なっていました。
1.Resolution(終了処理)
Deferredオブジェクトの、resolve、reject、notifyメソッドは、 コンテキストとしてundefinedを返すようになりました。従来は、呼び出し元のDeferredオブジェクトが内包するPromiseオブジェクトが返されていました。たとえば、次のサンプルの場合、以前は、trueとなっていました。
var defer = $.Deferred(); setTimeout(function() { defer.resolve(); }, 1000); // 1秒後に実行 defer.then(function(){ console.log(this == defer.promise()); // false(従来はtrue) });
なお、明示的に コンテキストを渡したい場合には、resolveWith、rejectWith、notifyWithメソッドを利用します。
var defer = $.Deferred(); setTimeout(function() { defer.resolveWith($("#test")); }, 1000); // 1秒後に実行 defer.then(function(){ console.log(this); // thisは、$("#test") });
2.コールバックの終了処理
2つめは、Deferredオブジェクトのthenメソッドの大きな変更です。
thenメソッドで指定したコールバック内で何らかの例外が発生すると、defferdオブジェクトの状態をreject(異常終了)に移行します。jQuery 3では、この例外を補足するためのcatchメソッドが追加されました。メソッドチェーンの最後に、catchメソッドを追加することが、 強く推奨されています。
従来では、thenメソッドのコールバックで例外が発生した場合、コード実行が中断されて、defferdオブジェクトの外側に伝播されていました。
var defer = $.Deferred(); setTimeout(function() { defer.resolve(); }, 1000); defer.then(function(){ sample(); // 未定義メソッドの呼び出し }).catch(function(){ console.log("error"); // 例外が補足されて、このコードが実行される。 });
promises/A+の仕様では、promiseオブジェクトは常に1つの値として最後まで遷移し、コールバックでは、コンテキストなしで実行することになっています。ところが、jQueryのDeferredでは、複数の値を扱うことも可能です。コンテキストや複数の値を受け取りたい場合は、従来のdoneやfailメソッドを使ってください。
なお、thenメソッドの実行は、document-readyと同様、非同期になりました。
// promises/A+準拠ではないthenメソッドの使い方 $.ajax("test.php").then( function( data, textStatus, jqXHR ) { console.log("OK"); }, function( jqXHR, textStatus, errorThrown ) { // エラー時の処理 console.log(errorThrown); }); // 従来と同じ挙動のコード $.ajax("test.php").done( function( data, textStatus, jqXHR ) { console.log("OK"); }).error( function( jqXHR, textStatus, errorThrown ) { // エラー時の処理 console.log(errorThrown); });
3.後方互換性
Deferredオブジェクトのdone、fail、pipeメソッドは、後方互換性のために従来と同じ挙動になっており、Promises/A+に準拠していません。もし、従来のコードのまま利用したい場合は、then、catchメソッドの代わりに、doneやfailなどのメソッドを使います。
$.whenメソッドの引数
$.whenメソッドの引数には、thenメソッドを持つ、Promise仕様に準拠した、いわゆるthenableオブジェクトならば何でも設定可能です。ECMAScript 6仕様のPromiseオブジェクトなども利用できます。
また、引数が2つ以上の場合と、引数がないか1つのときで、挙動を区別するようになりました。引数が2つ以上のときは、Promise.allメソッドと同じようにふるまい、引数がないか1つの場合は、Promise.resolveメソッドと同様になります。いずれも、戻り値は、新たに生成されたDeferredオブジェクトとなります。
var defer1 = $.Deferred(); setTimeout(function() { defer1.resolve(); }, 1000); var defer2 = $.Deferred(); setTimeout(function() { defer2.resolve(); }, 1500); // defer1, defer2の処理を待つ $.when(defer1, defer2).done(function(){ }); // Promise.allメソッドと同じ Promise.all([defer1, defer2]).then(function() { }); // 引数が1つのとき、Promise.resolveと同じ挙動 $.when(defer1).done(function(){ }); // Promise.resolveと同じ Promise.resolve(defer1).then(function() { });
$.whenメソッドの進捗通知を削除
jQuery 3では、$.whenメソッドの引数のDeferredオブジェクトから、戻り値のDeferredオブジェクトに対する進捗の通知は削除されました。Promises/A+の仕様には、進捗メッセージについての規定はありません。また従来より、この機能についてはドキュメント化されていませんでした。