非同期性 JavaScript:Promises
こんにちは、ゼノフィseoです。
JavaScript は単一のスレッドであり、コードは上から下に向けて実行されます。つまり、2つのコードが同時に実行されることは不可能です。例えば、外部のサーバーから JSON ファイルをダウンロードするかもしれません。そしてそのファイルを取得するまで待たなければいけません。スレッドをブロックする代わりに、非同期性 JavaScript を使用して、コード実行をストリームライン化させることができます。
きっと貴方はもう非同期性 JavaScript に関しては親しみがあると思います。Events (オブサーバーパターン)や Callbacks は非同期性コードの例です。例えば、Ext.Ajax リクエストを作ったり、ユーザーがボタンを押すと、アクションはキューにプッシュされ、それはイベントループと呼ばれます。JavaScript エンジンはコードが async ファクション(上から下まで)の後に実行されるまで、イベントループのプロセスを始めません。つまり、JavaScript のコードはマルチスレッドの様には見えていても、実際には違うということです。
コールバックは、主にコールした側に完了を通知する async オペレーション(例えばデータベースからデータをロード)がある時に使用されます。そういったファンクションをコールする時は、そこに他のファンクションをアーギュメントとしてパスすることができます。それは、何かが起きたかを認証します。コールバックの中にコールバックを設置するのは非常に素晴らしいソリューションであり、JavaScript の持っている強力な機能の1つだと言えます。しかし、もしコードが他の非同期コードを先に部分的にリクワイアしてくる場合は散らかってしまう場合があります。
例えば、ローカルストレージからユーザー設定を読み込むとします(これはコールバックです)。その設定に基づき、外部のサーバーからデーターベース接続でリクエストをします(これは2つ目のコールバックです)。その情報をスクリーン上にレンダリングする前に、データベースからまた別のものを取得します(これは3つ目のコールバックです)。
こういった手順は大きな企業アプリケーションを作っている場合はよく使われます。あなたのコードには、コールバックファンクションの中にコールバックファンクションが書かれ、その中にまたコールバックファンクションが書かれている場合があるでしょう。もし、これらのファンクションが様々なファイルに渡って存在していたら、1ヶ月後にこのコードを読むのは非常に難しいことが安易に尊像できるはずです。
ここで登場するのが JavaScript Promises です。これはより読みやすく、理解しやすいコードの書き方なのです。Promise はタスクの結果を代表するもので、完了していてもしなくても存在します。Promise がいつ作成されたかはわからない、値への契約のようなものです。そしてthen メソッドを持ったオブジェクトまたはファンクションです。”then” メソッドにより、アクションは無限にチェーン化することができます。そこが素晴らしい点です。
Promises は一般的に4つの状態があります。
- fulfilled – promise が成功した場合
- rejected – promise が失敗した場合
- pending – 現在進行中、もしくは fulfilled や rejected されていない状態
- settled – すでに fulfilled もしくはリジェクトされている状態
Promises は ECMASciprt 6 の一部であり、モダンブラウザの一部のクライアントと Node.js の最新版で使用可能となっております。以下のブラウザは JavaScript Promises に対応しておりません:Explorer 11 以下、Andriod 4.4以下、iOS Safari 7.1以下。
Ext JS 6 は Promises に対応しており、Promises A+ スペックにあたいしています。クラシックとツールキット両方に含まれています。Sencha Promises クラスをコールすることができます。これはネイティブ JavaScript 機能のラッパーとして存在しています。レガシーブラウザーは Sencha の提供するフォールバックを利用し、モダンブラウザはネイティブの機能を使用します。
以下は Sencha Promise オブジェクトをリターンするファンクションの例です:
requestUserSettings: function(){ return new Ext.Promise(function (resolve, reject) { //something asynchronous, like loading a store Ext.getStore(‘Settings’).load({ callback: function(records, operation, success) { if(success){ if(records.length > 0){ //when it’s ok resolve(records); } else { //still ok, but no results resolve(false); } } else{ //something bad happened reject(operation); } } }); }); } |
Ext.Promise
コンストラクターは1つのアーギュメントと、2つのパラメータを持ったコールバック(resolve と reject)をとります。コールバック内で何か非同期なことを行った場合(例えばユーザー設定をローカルストレージストアから取得など)、”resolve” をコールします。もし全てがうまくいった場合は結果をパスし、ダメだった場合は “reject” をコールし、何がダメだったかをパスします。
以下がその Promises の使用法です:
this.requestUserSettings().then(function(records) { //It’s ok. do something with the records }, function(err) { //oh no, something went wrong, display a nice error }); |
“then” は2つのアーギュメントをとります。成功時のコールバックと、失敗時のコールバックです。両方とも任意で行えるものであり成功時や失敗時のみにコールバックを追加するということも可能です。更なる async アクションを次次と並べ、thenメソッドをチェーン化することは無限にできます。
"then()"
インスタンスメソッドとは別に、Promise チェーン を終了させる方法(“done()”
)や、保留中のチェーンをキャンセル (“cancel()”
) する方法があります。また、チェーンに onCompleted コールバックを添付させる方法があります(“always()”
)。例えば、結果とは関係ないクリーンアップロジックなどです。また、失敗をハンドルするために、チェーン内の1つのアクションがリジェクトされた場合に、onRejecteed コールバックを添付することもできます(“otherwise()”
)。
Ext.Promise
に加え、Sencha は Ext.Deferred
も提供します。これは、Ext JS 6 内で新しい Promises を作成するためのメカニズムです。これら2つのコンストラクターの違いは、deferred コンストラクターでは、作成者は プログレスアップデートなど裏側の “behind the scenes” extras に直接アクセスすることができます。
また、最後となってしまいましたが、Sencha は Ext.Ajax
内に Promises サポートを統合しました。
Ext.Ajax.request()
は、今や Ext.data.request.Base から派生したクラスのインスタンスであり、then メソッドで使用することができます。以下のようなコードが書けるようになりました:
Ext.Ajax.request({ url: 'feed.json', }).then(function(response) { // use response }).always(function() { // clean-up logic, regardless the outcome }).otherwise(function(reason){ // handle failure }); |
追加の資料
JavaScript と Ext JS Promises を使用する方法や解説は他にもたくさん存在します。以下のリソースをご確認ください。