IEのサポートが終了になったので、JavaScriptのPromise・async・awaitの使い方を改めて試してみます。
Promise
Promiseオブジェクトは、非同期処理の完了や失敗などの状態を表します。
Promiseには以下の3つの状態があります。
- pending(待機): 処理前または処理中で、成功も失敗もしていない状態。
- fulfilled(履行): 処理が完了して成功した状態。
- rejected(拒否): 処理が完了して失敗した状態。
new Promise()で実際にPromiseオブジェクトを作成して、各状態を確認してみます。
const promise = new Promise((resolve, reject) => {}); console.log(promise); // 処理完了前なのでpendingの状態 const promise2 = new Promise((resolve, reject) => { // 何らかの処理 resolve(); // 処理成功時 }); console.log(promise2); // 処理が成功しているのでfulfilledの状態 const promise3 = new Promise((resolve, reject) => { // 何らかの処理 reject(); // 処理失敗時 }); console.log(promise3); // 処理が失敗しているのでrejectedの状態
1~2行目はPromiseオブジェクトを作成したのみで処理を完了していないのでpendingの状態です。
new Promise()の関数内で、処理成功時には第一引数に設定しているresolve()、処理失敗時には第二引数に設定しているreject()を実行することで、Promiseオブジェクトの状態をfulfilledやrejectedに変更できます。
Promiseのデモページ
次に処理が終わった後にthen()とcatch()につなぐ流れを試してみます。
処理成功の状態(fulfilled)になった後はthen()、処理失敗の状態(rejected)になった後はcatch()のコールバックが実行されます。
その際に、resolve()やreject()の引数に与えた値をコールバックで使用できます。
const promise = new Promise((resolve, reject) => { setTimeout(() => { resolve('A'); // 処理成功時 }, 1000); }); promise.then((data) => { console.log('resolveのテスト 処理成功', data); // こちらが実行される }).catch((error) => { console.log('resolveのテスト 処理失敗', error); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => { reject('B'); // 処理失敗時 }, 1000); }); promise2.then((data) => { console.log('rejectのテスト 処理成功', data); }).catch((error) => { console.log('rejectのテスト 処理失敗', error); // こちらが実行される });
1つ目の例では成功時のresolve()を実行しているのでthen()のコールバック、2つ目の例では失敗時のreject()を実行しているのでcatch()のコールバックが実行されています。
Promiseのデモページ2
Promiseオブジェクトを返す別の例として、fetchを試してみます。
const url1 = './data.json'; const test1 = fetch(url1); console.log(test1); // 処理完了前なのでpendingの状態 test1.then((response) => { console.log('fetchのテスト 処理成功'); // こちらが実行される console.log(test1); // 処理が成功しているのでfulfilledの状態 console.log(response); return response.json(); // jsonの中身を取得 }).then((data) => { console.log(data); }).catch((error) => { console.log('fetchのテスト 処理失敗'); console.log(test1); console.log(error); }); const url2 = './nothing.json'; // 存在しないファイル取得の場合 const test2 = fetch(url2); console.log(test2); // 処理完了前なのでpendingの状態 test2.then((response) => { console.log('fetchのテスト 処理成功'); // 存在しないファイル(404)だけどthenの方に入る console.log(test2); // fulfilledの状態 console.log(response); return response.json(); // jsonを取得していないのでエラーになり、catch()に入る (Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0) }).then((data) => { console.log(data); }).catch((error) => { console.log('fetchのテスト 処理失敗'); // fetchのエラーではなく、json()のエラーでここに入る console.log(test2); console.log(error); });
fetch()の時点ではHTTPレスポンス全体の取得になるので、jsonの中身を取得するためさらにjson()を使っています。
json()もPromiseオブジェクトを返すので、then()でさらに繋いでいます。
fetchのデモページ
fetchの注意点として、fetchのレスポンスが404の場合でもfulfilled(履行)の状態になるため、then()の処理に入ります。
上記2つ目の例でもjson()でのエラーで最終的にはcatch()に入っていますが、fetch()で取得した時点ではcatch()ではなくthen()の方に入っています。
これは仕様的にこのような挙動になっているようで、ネットワーク障害があった場合などにrejected(拒否)の状態になります。
async/await
asyncは非同期関数の宣言で使用され、その中でawaitを使用することができます。
awaitはPromiseから状態が返されるのを待つ際に使われます。
実際にasyncとawaitを使ってみます。
// Promiseを返す関数 const promiseFunc = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve('A'); }, 1000); }); } const asyncFunc = async () => { const result = await promiseFunc(); // promiseFunc() から値が返ってくるまで待つ console.log(result); // A } asyncFunc();
10行目でasyncを使って非同期関数の宣言を行い、関数内の11行目のpromiseFunc()がPromiseを返す関数なので、awaitで値が返ってくるまで待つようにしています。
asyncとawaitのデモページ
Promiseの時と同じように、fetchを使った例で試してみます。
const asyncFunc = async () => { const url = './data.json'; const result = await fetch(url); // fetch() から値が返ってくるまで待つ console.log(result); const json = await result.json(); // jsonの中身を取得 console.log(json); } asyncFunc();
意図したとおりの動作を確認できました。
asyncとawaitのデモページ2
コメントが承認されるまで時間がかかります。