約束は、単純に言えば、未来に終了するイベント(通常は非同期操作)の結果を保存するコンテナです。構文的には、Promise はオブジェクトであり、非同期操作のメッセージを取得できます。
Promise オブジェクトを使用すると、非同期操作を同期操作のフローで表現し、ネストされたコールバック関数を回避することができます。さらに、Promise オブジェクトは統一されたインターフェースを提供し、非同期操作の制御を容易にします。
Promise にはいくつかの欠点もあります:
- まず、Promise をキャンセルすることはできません。作成するとすぐに実行され、途中でキャンセルすることはできません。
- コールバック関数を設定しない場合、Promise 内部で発生したエラーは外部に反映されません。
- 保留中の状態では、現在の進行状況がどの段階にあるか(開始したばかりなのか、まもなく完了するのか)を把握することはできません。
Promise の特徴#
- オブジェクトの状態は外部の影響を受けません。Promise オブジェクトは非同期操作を表し、3 つの状態があります:保留中(pending)、成功(fulfilled)、失敗(rejected)。
- 状態が変化すると、二度と変化しなくなり、いつでもその結果を取得できます。Promise オブジェクトの状態が変化すると、成功するか失敗するかのいずれかであり、いずれの場合でも状態が固まり、解決済み(resolved)と呼ばれます。変更が発生した後でも、コールバック関数を追加すると、結果を取得できます。
使用法#
Promise インスタンスの作成#
Promise オブジェクトは、Promise インスタンスを生成するためのコンストラクタです。
const promise = new Promise(function(resolve, reject) {
if (/* 非同期操作が成功した場合 */){
resolve(value);
} else {
reject(error);
}
});
resolve 関数は、Promise オブジェクトの状態を「未完了」から「成功」に変更します(つまり、保留中から解決済みに変更します)。非同期操作が成功した場合に呼び出され、非同期操作の結果を引数として渡します。
reject 関数は、Promise オブジェクトの状態を「未完了」から「失敗」に変更します(つまり、保留中から拒否済みに変更します)。非同期操作が失敗した場合に呼び出され、非同期操作が発生したエラーを引数として渡します。
then メソッド#
次に、resolved 状態と rejected 状態のコールバック関数を指定するためにthenメソッドを使用します:
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 に変更するだけでクロスドメインが可能になります。