Promiseは、将来終了するイベント(通常は非同期操作)の結果を保持するコンテナです。構文的には、Promise はオブジェクトであり、非同期操作のメッセージを取得できます。
Promise オブジェクトを使用すると、非同期操作を同期操作のフローで表現し、ネストされたコールバック関数を回避することができます。さらに、Promise オブジェクトは統一されたインターフェースを提供し、非同期操作の制御を容易にします。
Promise にはいくつかの欠点もあります:
- まず、Promise はキャンセルできません。作成するとすぐに実行され、途中でキャンセルすることはできません。
- コールバック関数を設定しない場合、Promise 内部で発生したエラーは外部に反映されません。
- 保留中の状態では、現在の進行状況(開始したばかりか、まもなく完了するか)を把握することはできません。
Promise の特徴#
- オブジェクトの状態は外部の影響を受けません。Promise オブジェクトは非同期操作を表し、3 つの状態(進行中、成功、失敗)があります。
- 状態が変化すると、再び変化することはありません。いつでもその結果を取得できます。Promise オブジェクトの状態が変化すると、成功するか失敗するかのいずれかです。これらの状態が発生すると、状態は固定され、解決済みと呼ばれます。変更が発生した後でも、コールバック関数を追加すると、結果を取得できます。
使用法#
Promise インスタンスの作成#
Promise オブジェクトは、Promise インスタンスを生成するためのコンストラクタです。
const promise = new Promise(function(resolve, reject) {
if (/* 非同期操作が成功した場合 */){
resolve(value);
} else {
reject(error);
}
});
resolve 関数は、Promise オブジェクトの状態を「未完了」から「成功」に変更します(つまり、pending から resolved に変更します)。非同期操作が成功した場合に呼び出され、非同期操作の結果を引数として渡します。
reject 関数は、Promise オブジェクトの状態を「未完了」から「失敗」に変更します(つまり、pending から rejected に変更します)。非同期操作が失敗した場合に呼び出され、非同期操作で発生したエラーを引数として渡します。
then メソッド#
次に、thenメソッドを使用して resolved 状態と rejected 状態のコールバック関数を指定します:
promise.then(function(value) {
// 成功時の処理
}, function(error) {
// 失敗時の処理
});
最初の関数は、状態が rejected に変わったときに呼び出されます。2 番目の関数は、resolved の呼び出しです。2 番目の関数はオプションです。
then メソッドは、新しい Promise インスタンスを返します(注意:元の Promise インスタンスではありません)。そのため、then メソッドの後に別の then メソッドを呼び出すことができます。
promise.then(function(value) {
}.then(function (comments) {
console.log("resolved: ", comments);
}, function (err){
console.log("rejected: ", err);
});
catch メソッド#
エラーが発生した場合のコールバック関数を指定するために使用されます。
promise.then(function(value) {
// 成功時の処理
}, function(error) {
// 失敗時の処理
}).catch(function(error) {
// 発生したエラーの処理
console.log('エラーが発生しました!', error);
});
非同期操作がエラーをスローすると、状態が rejected に変わり、catch () メソッドで指定されたコールバック関数が呼び出され、エラーを処理します。また、then () メソッドで指定されたコールバック関数が実行中にエラーがスローされると、catch () メソッドでキャッチされます。
伝統的な try/catch ブロックとは異なり、catch () メソッドでエラー処理のコールバック関数を指定しない場合、Promise オブジェクトがスローしたエラーは外部のコードに伝播せず、何の反応もありません。
finally メソッド#
Promise オブジェクトの最終的な状態に関係なく、常に実行される操作を指定するために使用されます。
promise.then(function(value) {
// 成功時の処理
}, function(error) {
// 失敗時の処理
}).catch(function(error) {
// 発生したエラーの処理
console.log('エラーが発生しました!', error);
}).finally(function(){
alert("完了");
});
finally メソッドのコールバック関数は引数を受け取りません。つまり、最終的な状態がどのようなものであるかはわかりません。
Jquery、Ajax、Promise の例#
以前に Jquery と Ajax を学んだので、適当な API をテストしてみました:
const apiUrl = "https://suggest.taobao.com/sug?code=utf-8&q=电脑&callback=cb";
$("##bt").click(apiUrl,function(event){
const promise = new Promise(function(resolve,reject){
$.ajax({
url: event.data,
type:"get",
dataType:"jsonp",
headers: {
Accept: "application/json; charset=utf-8",
},
success: function(data) {
resolve(data);
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
reject(XMLHttpRequest,textStatus,errorThrown)
},
})
})
promise.then(function(data){
console.log(data);
let x = data.result;
for(let i = 0 ; i<data.result.length;i++){
console.log(x[i][0]);
}
},function(XMLHttpRequest, textStatus, errorThrown){
alert(XMLHttpRequest);
alert(textStatus);
alert(errorThrown);
}).catch(function(error){
console.log(error);
}).finally(function(){
alert("完了");
});
});
結果:
問題に遭遇したこと#
実際には、Promise の基本的な学習には大きな問題はありませんでしたが、Ajax リクエストの際にクロスドメインの問題に遭遇しました。具体的には:
Access to XMLHttpRequest at ‘ここに API が入る’ from origin ‘null’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
これは一体何なのでしょうか...
少し調べてみましたが、まずはクロスドメインの概念について説明します。
各ウェブサイトは、同じソースからのデータのみを読み取ることができます。ここでの同じソースとは、ホスト名(ドメイン)、プロトコル(http/https)、ポート番号の組み合わせを指します。明示的な許可がない限り、相手のリソースにアクセスしたり、書き込んだりすることはできません。これは、ブラウザの最も基本的で重要なセキュリティ機能であり、同じソースからのみ読み書きできます。
そして、Ajax の XMLHttpRequest は同一オリジンポリシーの制限を受けており、同じソース内のデータのみにアクセスできます。そのため、このエラーが発生します。
では、なぜこのタイプのリクエストはクロスドメインで動作するのでしょうか?
src 属性を持つ script、img、iframe、link などのタグは、同一オリジンポリシーに従う必要はありませんが、src を介して読み込まれるリソースに対しては、ブラウザが JavaScript の権限を制限しています。読み取りはできますが、書き込みはできません
以上を踏まえると、dataType を json から jsonp に変更するだけでクロスドメインが可能になります。