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
コメントが承認されるまで時間がかかります。