コールバック関数を利用すると、何度もコールバック関数が呼ばれて、ソースコードの入れ子構造が複雑になるという「コールバック地獄」という問題が発生してきました。
その問題を解決するのが、「ECMAScript2015」で採用されたPromiseオブジェクトというオブジェクトです。
Promiseオブジェクトの特徴
- 非同期処理を監視するためのオブジェクトです。
- 「ブラウザオブジェクト」ではなく、「JavaScript標準の組み込みオブジェクト」である。
Promiseオブジェクト使用の流れ
1.Promiseオブジェクトを返す関数を作る方法
非同期処理の対象となる関数でPromiseオブジェクトを返すように定義します。その際にPromiseクラスの中でコールバック関数(function(resoleve)〜)を定義するようにします。
1 2 3 4 5 6 7 |
function 関数(){ return new Promise(){ //コールバック関数 function(resolve){ } }.then(コールバック関数); } |
Promiseオブジェクトのコンストラクタは、下記2つの引数を取ります。
引数 | 説明 |
---|---|
resolve | 第一引数として取り、指定は、必須になります。処理結果が正常だった場合に、「then」によって設定した次の関数を呼びます。これもPromiseオブジェクトのメソッドになります。 |
reject | 任意です。処理結果が異常だった場合に実行させます。 |
thenメソッド
resolveメソッドを読んだ際に、このthenで設定したメソッドが呼ばれます。resolveメソッドの第一引数が、次にthenで呼ぶメソッドの引数になります。
用途
順番に非同期処理を実行したいかつ、前の処理の処理結果を次の処理に使いたい場合に使えます。
実装例
1 2 3 4 5 6 7 8 9 10 11 |
function task1(){ return new Promise(function (resolve, reject) { console.log('test') //引数として渡された「task1」を次のthenメソッドのコールバック関数内の第一引数(result1)として渡す。 resolve('task1'); }).then((result1) => { console.log(result1) }); } task1(); |
Promiseの中の処理結果をresolveによってthenに渡しています。逆にPromiseのコールバック関数の中にresolveメソッドを定義していない場合は次のthenを呼ぶ事ができないので例えresolveに渡す引数がなかったとしても必ずresolveメソッドを定義するようにしましょう。
2.関数同士をつなぐ方法(メソッドチェーン)
1 |
関数1.then(成功時に呼ぶ関数2,失敗時に呼ぶ関数2).then(成功時に呼ぶ関数3,); |
普通のコールバック関数の入れ子に比べて非常に読みやすいですよね。
元々、jQueryや、AngularJSといったライブラリにはこのような機能が付属されていましたが、ECMAScript2015より、標準のJavaScriptでも利用できるようになったのです。
thenメソッド
Promiseオブジェクトでの結果を受けるメソッドです。
用途
いちいち、非同期処理用の関数を宣言しなくても、関数の中で非同期処理を呼べたりします。
実装例
1 2 3 4 5 6 |
new Promise(function(resolve){ console.log('test') resolve(); }).then(function(resolve){ console.log('test2') }) |
実装例2
成功時の処理と、失敗時の処理で分岐させる例です。なお、Promiseクラス内のコールバック関数で次のthenの引数にする場合はresolveの第一引数に指定する仕様でしたが、thenメソッドからthenメソッドに引数を渡す場合はreturnで渡す事になるので注意です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
new Promise(function(resolve){ console.log('test') resolve('戻り値'); }).then(function(resolveからの引数){ console.log('成功時') return 戻り値; },function(thenからの引数){ console.log('失敗時') return 戻り値; }).then(function(thenからの引数){ console.log('成功時2') },function(){ console.log('失敗時2') }) |
実装例3
なお、thenのコールバック関数の中でPromiseオブジェクトを改めて返したとしても同じ挙動になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
hidouki().then(text => { // console.log("あ"); console.log(text); return "かきくけこ" }).then(text => { return new Promise(resolve => { setTimeout(() => { resolve("たちつてと") }, 100) }); }).then(a => { console.log(a); //たちつてと }); |
Promise.all
上記thenをつなげる例でも少し冗長に見える場合があります。さらにすっきりさせたい場合は下記のように記述します。最終的なコールバックの連続の結果はthenで受けます。
1 2 3 4 5 6 7 |
Promise.all([] コールバック関数1, コールバック関数2 ]) //処理結果 .then((([引数]) =>{ }); |
catchメソッド
catchメソッドを使うとrejectを実行した結果を捕まえる事が可能です。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function hidouki() { return new Promise((resolve, reject) => { setTimeout(() => { reject("エラー発生"); }, 100); }); } hidouki().then(text => { console.log(text); }) .catch(error => { console.log(error); }); |
Exceptionでも呼ぶ事が可能
1 2 3 4 5 6 7 8 9 10 11 12 |
function hidouki() { return new Promise((resolve, reject) => { throw new Exception("エラー発生"); }); } hidouki().then(text => { console.log(text); }) .catch(error => { console.log(error); //ReferenceError: Exception is not defined return "finallyに渡す"; }) |
finally
catchの後にthenをつなげばJavaのtry 〜 catchでいうfinallyと同じ役割の処理を記述する事が可能です。catchメソッドのコールバック関数内でreturn文を使う事でfinallyに引数を渡す事が可能です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
function hidouki() { return new Promise((resolve, reject) => { setTimeout(() => { reject("エラー発生"); }, 100); }); } hidouki().then(text => { console.log(text); }) .catch(error => { console.log(error); return "Finallyに渡す"; }).then(text => { console.log("Finally"); console.log(text); //Finallyに渡す }); |
静的なresolveメソッド
下記のように単純にPromiseからthenに対して引数を渡すだけのメソッドを使う場合は静的メソッドのresolveメソッドを使うと楽です。
1 2 3 4 5 6 7 8 |
function hidouki() { return new Promise((resolve, reject) => { resolve("渡す"); }); } hidouki().then(text => { console.log(text); }) |
Promiseオブジェクトの静的メソッドになるのでnewする必要はありません。
1 2 3 4 5 6 |
function hidouki() { return Promise.resolve("渡す"); }; hidouki().then(text => { console.log(text); //渡す }) |
この記事へのコメントはありません。