async と awaitに関して
どうもフロントエンドエンジニアのoreoです。前回の「Promiseに関して」の続きとして、async、awaitと例外処理に関して記載します!
1 async と awaitとは?
async、awaitを利用すると、Promiseをさらに直感的に記述することができます。
-
async-
asyncを使うと、Promiseオブジェクトを返却する関数を宣言できます。
-
-
await-
Promiseオブジェクトをreturnする関数の前にawaitをつけると、Promiseがresolveまたはrejectされるまで待機することができます。待機中は、await以降の処理は中断され、待機が終わると以降の処理が再開されます。 -
awaitは、async内で使用します。
-
2 基本的な使い方
2-1 Promise構文の復習
「Promiseに関して」で、記載したPromiseチェーンは以下ex1~ex2のようなものでした。
関数appleは、Promiseを用いて、非同期処理が実装されています。
【ex1】
function apple(num) {
return new Promise(function (resolve) {
setTimeout(function () {
console.log(`りんごは${num}個`);
num += 1;
resolve(num);
}, 1000);
});
}
Promiseを使った場合には、.thenメソッドで、非同期処理の後に実行したい処理を繋げることができました。
【ex2】
/**
* 以下が1秒毎に出力
* りんごは1個
* りんごは2個
* りんごは3個
* 終了! ※これは「りんごは3個」と同時に出力
*/
apple(1)
.then(function (num) {
return apple(num);
})
.then(function (num) {
return apple(num);
})
.then(function (num) {
apple(num);
console.log("終了!")
})
2-2 async、awaitを使う
このex2の処理をasync、awaitを使って、簡略化するとex3のようになります。
まず、asyncを使って関数appleAsyncを定義し、その中でawaitを使って関数appleに引数1を渡します。await apple(1)では、関数appleで、resolveされた値が返されます。その値を変数valに代入し、続いてawait apple(val)をvalに再代入していくことで、ex2と同じ挙動をとることができます。
ここで、awaitをつけた処理は、resolveが呼ばれるまで(非同期処理が終わるまで)、待機状態となり、それ以降の処理は中断されます。全ての非同期処理が終了した後に、console.log("終了!")は実行されます。
【ex3】
/**
* 以下が1秒毎に出力
* りんごは1個
* りんごは2個
* りんごは3個
* 終了! ※これは「りんごは3個」と同時に出力
*/
async function appleAsync(){
let val = await apple(1) //関数appleでresolveされた値(ここでは2)がvalに代入される
val = await apple(val) //3がvalに代入される
val = await apple(val) //4がvalに代入される
console.log("終了!")
}
appleAsync()
ex3での関数appleAsync()は、Promiseを返すため、関数appleAsync()を実行した後に.thenメソッドを繋げることができます。
【ex4】
/**
* 以下が出力。
* りんごは1個
* appleAsyncから2が取得できます
*/
async function appleAsync(){
let val = await apple(1) //関数appleでresolveされた値(ここでは2)がvalに代入される
return val
}
appleAsync()
.then(function (val) {
console.log(`appleAsyncから${val}が取得できます`);
})
また、関数appleAsync()内でthrow new Error()を呼ぶと、.catchに繋げることも可能です。
【ex5】
async function appleAsync() {
let val = await apple(1);
throw new Error();
return val;
}
appleAsync()
.then(function (val) {
console.log(`appleAsyncから${val}が取得できます`);
})
.catch(function (e) {
console.error(e); //Error !!!
});
3 使いどころ
fetchなどでのデータ取得やエラーハンドリングでよく使いますね。
3-1 fetchでデータを取得する
fetchメソッドを使えば、HTTPリクエストを発行して、サーバーなどからデータを取得できます。fetchメソッドは、Promiseオブジェクトを返却します。
ここで下記のようなfruits.jsonからfetchメソッドを用いてデータを取得したいと思います。
【fruits.jsonファイル】
[
{
"name": "Apple",
"price": 150
},
{
"name": "Orange",
"price": 100
}
]
fetchメソッドはPromiseオブジェクトを返すので、.thenを使ってfetchメソッドでresolveされた値を取得できます(ex6)。
【ex6】
fetch("fruits.json").then((data) => {
console.log(data)
})
resolveされた値を見てみると、HTTPステータスコードなどが格納されたオブジェクトが確認できます。

プロトタイプを見てみると、さまざまなメソッドが準備されています。

それらの中で、.jsonメソッドを使うとJSONデータを取得できます(ex7)。
【ex7】
/**
* 以下が出力
* [{"name": "Apple","price": 150},{"name": "Orange","price": 100}]
*/
fetch("fruits.json").then((data) => {
return data.json() //jsonを取得して、returnする
}).then((json)=>{
console.log(json)
});
これらをasync、awaitを使って書き換えると、【ex8】のようになります。
【ex8】
/**
* 以下が出力
* [{"name": "Apple","price": 150},{"name": "Orange","price": 100}]
*/
async function fetchFruits() {
const data = await fetch("fruits.json");
const fruits = await data.json(); //.jsonは、Promiseをreturnするのでawaitする
console.log(fruits);
}
fetchFruits();
3-2 例外処理(エラーハンドリング)
例外処理とはエラーが発生した場合に行う処理のことで、【ex9】のようにtry、catch、finallyを用いて実装します。
tryブロックの中でエラーが発生した場合に、それ以降の処理は行わずcatchブロックに移行して、エラーハンドリングを行ないます。
【ex9】
try {
//tryの処理でエラーを投げるとそれ以降の処理は行わずcatchに移行する
throw new Error();
} catch (e) {
// エラー時の処理(エラーハンドリング)を行う
console.error(e)
} finally {
//try、catchに関わらず行う終了処理を記載
}
3-1で作成したfruits.jsonをfetchするような処理で例外処理を実装するとex10のように記載できます。ここでは、データを取得する関数としてfetchFruitsを定義し、fetchが失敗した場合は、throw new Error("データ取得失敗!");、jsonが空だと、throw new Error("データがないよ!");を投げます。
また、取得したデータを出力する関数として、outputFruitsを定義します。tryブロックの中でfetchFruits関数を実行し、throw new Error()が実行されると、そのエラーをcatchブロックのconsole.error(e);でエラー内容を確認できます。
【ex10】
//fruitsを取得する関数
async function fetchFruits() {
const data = await fetch("fruits.json");
//data.okには、fetchが成功したかどうかのbooleanが格納されている
if (data.ok) {
const fruits = await data.json();
//jsonが空だとエラー投げる
if (!fruits.length) {
throw new Error("データがないよ!");
}
return fruits;
}else{
throw new Error("データ取得失敗!");
}
}
//fruitsを出力する関数
async function outputFruits() {
try {
const fruits = await fetchFruits();
console.log(fruits);
} catch (e) {
console.error(e);
} finally {
console.log("終了です");
}
}
outputFruits();
最後に
前回のPromiseに続いて、async、awaitや例外処理を整理しました。ここら辺は押さえておきたい内容ですね。
Discussion