#Promiseとは
非同期処理のコールバック関数をわかりやすく記述する方法。メソッドチェーンにすることでコールバック地獄を回避することができる。
#Promiseの使い方
###Promiseオブジェクトの作り方
newを使ってPromiseのインスタンスを作成し、その「返り値」としてPromiseオブジェクトを取得する。
var result = new Promise( )
Promise()の引数には関数を設定し、その中で行いたい処理を記述する。
例えば、日付を取得するPromise処理は次のようになる。
var result = new Promise(function(resolve) {
resolve(new Date);
})
この例では、Promiseの引数に関数を設定して「new Date」で日付を取得している。
関数の引数「resolve」に取得したい値を設定することで、非同期処理の結果を格納することができるようになる。
最終的に変数「result」にはPromiseオブジェクトが格納される。
###「then」を使ったコールバック処理を行う方法
Promiseは「then()」を使うことでコールバックのような処理を実現できます。
先ほど作成したPromiseは最終的に変数「result」にPromiseオブジェクトが格納された。
このPromiseオブジェクトから、例えば日付データを取得して西暦を取得するには次のよう記述できる。
result.then( (data) => console.log(data.getFullYear()) );
2020
#チェーンを使ったPromise処理
###Promise処理を連結する方法
コールバック地獄とは以下のように複雑でデバックが困難な状態です。
getDate(function(data1) {
getSomething1(function(data2) {
getSomething2(function(data3) {
getSomething3(function(data4) {
});
});
});
しかし、Promise処理の「then」は以下のようにチェーンで連結することができる。
result
.then(function(data) { return getSomething1(item) })
.then(function(item) { return getSomething2(item) })
.then(function(item) { getSomething3(item) })
これにより階層構造が複雑化しにくいため、コードの見通しもよくなる。
また、注意点として「then」を繋げて処理をする場合は、処理の最後に「return」で別のPromiseオブジェクトを返すようにする。
###「all」で複数のPromise処理を連結する方法
以下のように「all」を使えば、Promise処理を同時に実行してすべて完了したら次の処理を行うことができる。
Promise.all([
something1(),
something2(),
something3()
])
このように「all()」の中に配列として同時に実行したい関数を指定する。
すべて完了したあとに次の処理を記述したければ「then」で繋げられる。
Promise.all([
something1(),
something2(),
something3()
])
.then(function(data) {
console.log(data);
console.log('すべての処理が終了しました');
})
["1つ目の処理", "2つ目の処理", "3つ目の処理"]
すべての処理が終了しました
#「Promise」でAjax通信
###PromiseでサーバーからGET通信する方法
「XMLHttpRequest()」を使ったAjax通信を利用し、「JavaScript」のリポジトリ総数を取得してみる。
以下のサンプルを見てみよう。
//GitHubからJavaScriptのリポジトリ総数を取得するURL
var url = 'https://api.github.com/search/repositories?q=javascript';
function get(url) {
return new Promise(function(resolve) {
//インスタンスを作成する
var xhr = new XMLHttpRequest();
//指定のサーバーとの通信を開始する
xhr.open('GET', url);
xhr.send();
//通信が正常に終了したかを確認する
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && xhr.status === 200) {
var result = JSON.parse(xhr.responseText);
//サーバーから取得したデータを使う
resolve( result.total_count );
}
}
});
}
ポイントは「XMLHttpRequest()」を使ったコードを丸ごとPromise処理で囲んでいる点です。
そして、GitHubからJSONデータを取得できるので、その中からリポジトリ総数だけを取得する。
あとは、それを「resolve()」に指定すればPromise処理が完成する。
get(url)
.then(function(response) {
console.log("成功", response);
})
成功 385083
このように「then」から受けとる「response」には、リポジトリ総数が格納されている。
#エラーハンドリングについて
###Promiseでエラー処理を条件分岐する方法
Promiseが正しく動作せずエラーが発生した場合の処理も一緒に記述する。
return new Promise(function(resolve, reject) {
・
・
・
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && xhr.status === 200) {
resolve('正常に処理されました!');
} else if(xhr.status !== 200) {
reject('エラーです!');
}
}
})
これまで、Promiseが正常に実行された場合は「resolve」を使っていましたが、エラー処理の場合は「reject」を使う。
基本的に「resolve / reject」はペアで一緒に使われる。
実行するには次のように記述する。
get(url)
.then(function( response ) {
console.log( response );
}, function( error ) {
//エラー処理を記述する
console.error( error );
})
これまでは「then」の第1引数だけを使っていたが、第2引数に「reject()」のPromiseが返るようになっている。
そのため、エラー処理を記述する場合は上記サンプルのように第2引数を使う。