【JavaScript】 async/await で非同期処理をわかりやすく記述する
IT技術

async/await という仕組み

本記事では、 JavaScript の非同期処理を扱うための async/await という仕組みについて取り上げます。
async/await は、 Promise を利用した非同期処理をよりわかりやすく記述できます。
本題へ進む前に Promie の復習をしておきましょう!
Promise の復習
async/await は、 Promise の仕組みを利用しているため、Promise への理解が大切だからです。
非同期関数を作成するときに Promise が利用できます。
1return new Promise((resolve, reject) => {});Promiseの状態
Promise には、以下の状態があります。
apending: 初期状態、実行中。成功も失敗もしていない
fulfilled: 処理が完了した状態。then メソッドを呼び出す
rejected: 処理が失敗した状態。then メソッドと catch メソッドを呼び出す。
- resolve() が呼ばれると、
fulfilledの状態になります。 - rejected() が呼ばれると、
rejectedの状態になります。
Promise の then
then() は、 Promise のインスタンスの状態が fulfilled となったときに実行する関数を登録できるインスタンスメソッドです。
then() は、以下のように定義されています。
1Promise.prototype.then(onFulfilled, onRejected)onFulfilled:fulfilledの状態のとき(resolve が呼ばれたとき)に実行される関数onRejected:rejectedの状態のとき(reject が呼ばれたとき)に実行される関数
Promise の catch
reject() とは、 Promise のインスタンスの状態が rejected となったときに実行する関数を登録するインスタンスメソッドです。
1Promise.prototype.catch(onRejected)onRejected:rejectedの状態のとき(reject が呼ばれたとき)に実行される関数
Promise の関連記事
async とは
非同期関数を定義する関数定義です。
async function で非同期関数を定義できます。
async function は Promise インスタンスを返却します。
1async function asyncFunc(/*引数*/) {
2 // 処理
3 return value;
4}上記の例で定義した asyncFunc は、以下の関数と同義です。
1// 通常関数で Promise を返却する
2function promiceFunc(/*引数*/) {
3 return new Promise((resolve, reject) => {
4 resolve(value);
5 });
6}async function は、以下のことを行います
async function中でreturnされたとき、戻り値でPromise.resolveしますasync function中でPromiseがreturnされたときは、その返り値のPromiseをそのまま返します。async function中で例外や値をthrowしたときは、その値でPromise.rejectをします。
async function を実際に使ってみる
async function を実際に書いてみて、どのように動作するか確認していきましょう。
async function の中で return
async function で定義された関数内でreturnされると、その return の返り値で Promise.resolve が実行されます。
つまり、 async function で定義された関数は then() でつなげることができます。
1async function asyncFunc() {
2 // 処理
3 return "resolve";
4}
5
6// then() で return の値を受け取ることができる
7asyncFunc().then(value => console.log(value)); // => resolveasync function の中で Promise を return
async function で定義された関数内で Promise を return すると、そのPromise がそのまま返却されます。
ですので、then() や catch() で処理を受けることができます。
1async function rejectFunc() {
2 // reject を呼ぶ
3 return Promise.reject(new Error("エラーです"));
4}
5
6rejectFunc().catch(error => console.log(error.message));
7// 実行結果 => エラーですaysnc function の中で throw
async function で定義された関数内で throw された場合、その throw の値でPromise.reject が実行されます。
ですので、catch() でエラー処理を行えます。
1// throw
2async function throwFunc() {
3 throw new Error("エラーが発生");
4}
5
6// catch() で例外処理を行える
7throwFunc().catch(error => console.log(error.message)); // => エラーが発生上記の例で見てもらったらわかるのですが、結局 async function は Promise を返すだけの関数です。
外部の関数からaync function を呼び出すときは、Promise を返却する非同期関数であると認識していればいいです。
await 式
await 式の使いかた
async function では、await 式を利用できます。
await 式を利用すると、指定した Promise の結果が返されるまで処理を待機します。
Promise の状態が、 fulfilled または rejected になると、処理を続行します。
1// setTimeout を Promise でラップ
2const wait = ms => new Promise(resolve => setTimeout(() => resolve(), ms));
3
4// async
5async function awaitFunc() {
6 console.log(1);
7 await wait(3000); // Promise が返ってくるまで awaitで 処理停止
8 console.log(2); // 約3秒経過に表示
9}
10
11awaitFunc();wait() 関数は、setTimeout() を Promise でラップした非同期関数です。
そのため、普通は wait() 関数で処理が止まることはありません。
await式を利用しなければ、ターミナルにはすぐに「1 2」と表示されます。
ですが、await 式を利用しているため、上記のコードではwait() 関数が処理を終了するまで、次の処理 (console.log(2) ) が評価されません。
ですので、上記のコードを実行してみると、コンソールに 1 が表示されてから、約 3 秒経過した後、2 が表示されます。
await 式の嬉しいところ
await 式を利用することで、非同期処理でありながら、同期処理のように上から順番に処理するように記述できます。
非同期関数でありながら、見通しのよいコードが記述可能となります。
1// 約一秒後、受け取った値を2倍で返す非同期関数
2const promiseFunc = value => {
3 return new Promise((resolve, reject) => {
4 setTimeout(() => {
5 resolve(value * 2);
6 }, 1000);
7 });
8};
9
10// 非同期関数
11async function asyncFunc() {
12 // await 式によって順番に処理される
13 const a = await promiseFunc(1);
14 const b = await promiseFunc(2);
15 const c = await promiseFunc(3);
16
17 return a + b + c;
18}
19
20asyncFunc().then(value => {
21 console.log(value); // => 12
22});サンプルコード中の関数 promiseFunc() は、処理に約1秒かかります。
asyncFunc() では await 式で処理が終了するのを待機して、各変数に格納しています。
await 式の利用によって同期処理のような記述になっています。
※ 1つずつ関数の終了を待っているため、このコードは実行に約3秒以上かかります。このままでは問題なので、次の章で効率的に処理できるよう改善します。
async の中で Promise.all()
await を使うと、非同期処理を同期処理のように記述できわかりやすいです。
ですが、連続で記述された非同期処理を1つずつ待ってしまってはムダな待ち時間が発生します。
その問題を解決するには、 Promise.all() というメソッドを利用します。
Promise.all()
Promise.all() は、引数に非同期関数の配列を受け取り、Promise インスタンスを返すメソッドです。
非同期処理の結果は、配列として戻ってきます。
Promise.all() に引数として与えられた非同期関数の配列は、並行して実行されます。
そのため、非同期処理を効率的に処理できます。
すべての非同期関数の処理が終わるとPromise.all() で返ってくる Promise インスタンスの状態は fulfilled になります。
await 式は返された Promise インスタンスが fulfilled になるまで待機します。
つまり await 式は、Promise インスタンスを返却する処理と組み合わせることができます。
よって await 式は、 Promise.all() メソッドとも組み合わせることができます。
サンプルコード
1const promiseFunc = value => {
2 return new Promise((resolve, reject) => {
3 setTimeout(() => {
4 resolve(value * 2);
5 }, 1000);
6 });
7};
8
9// 非同期関数
10async function asyncFunc() {
11 // 並行して処理が実行され、全ての処理が終わるまで待機
12 const values = await Promise.all([
13 promiseFunc(1),
14 promiseFunc(2),
15 promiseFunc(3)
16 ]);
17
18 console.log(values);
19}
20
21asyncFunc();[結果]
[ 2, 4, 6 ]
サンプルコード中の関数 promiseFunc() は処理に約1秒かかり、他の変数やデータベースに影響を与えない独立した関数です。
並行して処理を行っても問題がないので、Promise.all() で実行しています。
そして、実行結果を await 式で受け取って、console.log でターミナルに表示しています。
実行してみると、約1秒で処理が終了します。
前の章では同じような処理に3秒かかっていたので、改善できています。
このように、互いに影響を及ぼさない非同期処理は、Promise.all() を利用することで効率的に処理できます。
await 式の中で例外
await 式の中で、 Promise が rejected になった場合、その場でエラーを throw します。
async function 内で発生した例外は自動的にキャッチされます。
何も処理しなければ、async function が rejected な Promise を返します。
サンプルコード
以下サンプルコードです。
1async function asyncFaile() {
2 // await 式で例外発生
3 const value = await Promise.reject(new Error("エラーです"));
4}
5
6// asyncFunc は例外を自動的にキャッチ
7asyncFaile().catch(error => console.log(error.message)); // => エラーですエラーが throw されるので try...catch でエラーをキャッチすることもできます。
1async function asyncFaile() {
2 // await 式で例外発生
3 try {
4 const value = await Promise.reject(new Error("エラーです"));
5 } catch (error) {
6 console.log(error.message); // => エラーです
7 }
8}
9
10// asyncFaile の中で 例外処理がされているので、catch()は呼ばれない
11asyncFaile()
12 .then(() => console.log("呼ばれる")) // => 呼ばれる
13 .catch(error => console.log("呼ばれない"));async function 内ではエラーを throw していないので、fulfilled な Promise が返されることになり、asyncFaile().then は呼ばれますが、asyncFaile().catch は呼ばれません。
もし、上位の関数でエラー処理したい場合はcatchしたのち、再び throw してください。
1async function asyncFaile() {
2 // await 式で例外発生
3 try {
4 const value = await Promise.reject(new Error("エラーです"));
5 } catch (error) {
6 throw error;
7 }
8}
9
10// asyncFaile で エラーが throw されるので、catch で受ける。
11asyncFaile()
12 .then(() => console.log("正常処理")) // 呼ばれない
13 .catch(error => console.log(error.message)); // => エラーですasync/await の対応状況
Chrome、Firefox、Edge、Safari といった最新ブラウザは対応済みです。
IE11 は未対応です。
ECMAScript 2016+ compatibility table
もし、IE もサポートに含めるのなら、Babel でトランスパイルする必要があります。
さいごに
以上、async/await についてでした。
それでは、最後に、今回の内容をまとめてみます!
- async/await 式で非同期処理を扱える
- async function で定義された関数は、Promise インスタンスを返す
- await 式は、Promise インスタンスが fulfilled の状態になるまで、次の処理を評価しない
- await 式は、Promise.all のような Promise インスタンスを返す処理と組み合わせることができる
非同期処理の async/await についてでした。
本記事が JavaScript のコード作成のお役にたてたら幸いです。
こちらの記事もオススメ!
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ
「好きを仕事にするエンジニア集団」の(株)ライトコードです! ライトコードは、福岡、東京、大阪、名古屋の4拠点で事業展開するIT企業です。 現在は、国内を代表する大手IT企業を取引先にもち、ITシステムの受託事業が中心。 いずれも直取引で、月間PV数1億を超えるWebサービスのシステム開発・運営、インフラの構築・運用に携わっています。 システム開発依頼・お見積もり大歓迎! また、現在「WEBエンジニア」「モバイルエンジニア」「営業」「WEBデザイナー」を積極採用中です! インターンや新卒採用も行っております。 以下よりご応募をお待ちしております! https://rightcode.co.jp/recruit







