async/awaitを利用することでPromiseの操作を簡潔に記述できます。JavaScriptで非同期処理を実装する際はasync/awaitを利用するケースがほとんどです。
今回はasync/awaitの概要についてまとめたので紹介をします。
なお、async/awaitを理解するにはPromiseの知識が必須です。
Promiseの概要についてはJavaScriptの非同期処理で理解必須!Promiseの概要・挙動まとめで紹介をしています。
目次
asyncについて
関数にasync
をつけることで非同期関数となります。
ここではasyncを利用した関数(async関数)の概要について紹介します。
async関数はPromiseオブジェクトを返す
async関数でreturn
をした場合fulfilled
なPromiseオブジェクトが返されます。
一方、async関数で例外がthrowされた場合はrejected
なPromiseオブジェクトが返されます。
const asyncFulfilled = async() => {
return 'fulfilled'
}
asyncFulfilled()
.then(result => console.log(`Resut: ${result}`))
.catch(message => console.log(`NG! ${message}`))
// Resut: fulfilled
const asyncRejected = async() => {
throw new Error('rejected')
}
asyncRejected()
.then(result => console.log(`Resut: ${result}`))
.catch(message => console.log(`NG! ${message}`))
// NG! Error: rejected
awaitについて
async/awaitのawaitの概要について紹介します。
awaitはasync関数の中でのみ利用可能
awaitはasync関数内に定義されているPromiseの結果(成功 or 失敗)を待つ演算子です。
つまり、awaitはasync関数の中でのみ利用が可能です。
async関数以外でawaitを利用すると以下のようにエラーが発生します。
const asyncExample = (number) => {
console.log(await Promise.resolve(number))
}
asyncExample(2)
// console.log(await Promise.resolve(number))
// ^^^^^
//
// SyntaxError: missing ) after argument list
async関数の中であればawaitが使えます。
const asyncExample = async(number) => {
console.log(await Promise.resolve(number))
}
asyncExample(2)
// 2
awaitはfulfilledなPromiseオブジェクトのデータを取得する
たとえば『入力値を2倍にする(ただし、入力値が10以上だったら例外を発生させる)』というメソッドがPromiseを利用して実装されていたとします。
当該メソッドの実行結果をasync/awaitを利用して取得するには以下のようになります。
const double = (number) => {
return new Promise((resolve, reject) => {
(number < 10) ? resolve(number * 2) : reject(new Error(`${number}は10以上の数です`));
})
}
const asyncDouble = async(number) => console.log(await double(number))
asyncDouble(2)
// 4
async関数でPromiseオブジェクトを返す場合は以下のようになります。
fulfilled
なPromiseオブジェクトがasync関数から返されるので、then()
を利用してデータを取得します。
const double = (number) => {
return new Promise((resolve, reject) => {
(number < 10) ? resolve(number * 2) : reject(new Error(`${number}は10以上の数です`));
})
}
const asyncDouble = async(number) => await double(number)
asyncDouble(2).then(result => console.log(`Resut: ${result}`))
// Resut: 4
async/awaitの部分(asyncDouble
メソッド)をthen()
で書き直すと以下のようになります。
const double = (number) => {
return new Promise((resolve, reject) => {
(number < 10) ? resolve(number * 2) : reject(new Error(`${number}は10以上の数です`));
})
}
const doubleFunction = (number) => double(number).then((result) => result)
doubleFunction(2).then(result => console.log(`Resut: ${result}`))
// Resut: 4
awaitで取得したPromiseオブジェクトがrejectedの場合、例外がthrowされる
以下の2つのコードは等価になります。
// awaitでrejectedなPromiseオブジェクトを受け取った場合
const asyncAwaitRejected = async() => {
await Promise.reject(new Error('rejected'))
}
// 上記のコードは以下のように解釈される
const asyncAwaitRejected = async() => {
throw new Error('rejected')
}
awaitで取得したPromiseオブジェクトがrejected
の場合、async関数内で例外がthrowされることになります。
つまり、async関数はrejected
なPromiseオブジェクトを返すことになります。
rejected
なPromiseオブジェクトがasync関数から返されるので、catch()
を利用してデータを取得します。
const asyncAwaitRejected = async() => {
await Promise.reject(new Error('rejected'))
}
asyncAwaitRejected().catch(error => console.log(error.message))
// rejected
たとえば、先ほど紹介した『入力値を2倍にする(ただし、入力値が10以上だったら例外を発生させる)』というメソッドの利用時に例外が発生した場合、async関数の結果は以下のようになります。
const double = (number) => {
return new Promise((resolve, reject) => {
(number < 10) ? resolve(number * 2) : reject(new Error(`${number}は10以上の数です`));
})
}
const asyncDouble = async(number) => await double(number)
asyncDouble(10)
.then(result => console.log(`Resut: ${result}`))
.catch(message => console.log(`NG! ${message}`))
// NG! Error: 10は10以上の数です
async/awaitのエラーハンドリング方法
async/awaitで利用されるエラーハンドリングの方法を2つ紹介します。
try-catchパターン
awaitが実行される箇所をtry-catchで囲む方法です。
一般的なエラーハンドリング方法ですが、『tryのスコープ外で戻り値を利用する場合、letの宣言が必要』『tryのスコープが広くなりすぎると例外発生箇所の特定が難しくなる』などのデメリットがあります。
const double = (number) => {
return new Promise((resolve, reject) => {
(number < 10) ? resolve(number * 2) : reject(new Error(`${number}は10以上の数です`));
})
}
const asyncDouble = async(number) => {
let result
try {
result = await double(number)
} catch(error) {
throw new Error(error.message)
}
return result
}
asyncDouble(2)
.then(result => console.log(`Resut: ${result}`))
.catch(message => console.log(`NG! ${message}`))
// Resut: 4
asyncDouble(10)
.then(result => console.log(`Resut: ${result}`))
.catch(message => console.log(`NG! ${message}`))
// NG! Error: 10は10以上の数です
await-catchパターン
awaitを利用している箇所にcatch()
を追加する方法です。
await-catchパターンではPromiseがrejected
になった場合のみ例外が発生するため、例外箇所を特定しやすいというメリットがあります。
const double = (number) => {
return new Promise((resolve, reject) => {
(number < 10) ? resolve(number * 2) : reject(new Error(`${number}は10以上の数です`));
})
}
const asyncDouble = async(number) => {
const result = await double(number).catch(error => {
throw new Error(error.message)
})
return result
}
asyncDouble(2)
.then(result => console.log(`Resut: ${result}`))
.catch(message => console.log(`NG! ${message}`))
// Resut: 4
asyncDouble(10)
.then(result => console.log(`Resut: ${result}`))
.catch(message => console.log(`NG! ${message}`))
// NG! Error: 10は10以上の数です
まとめ
- returnをしたasync関数はfulfilledなPromiseオブジェクトを返す
- 例外が発生したasync関数はrejectedなPromiseオブジェクトを返す
- awaitはasync関数の中でのみ利用可能
- awaitはfulfilledなPromiseオブジェクトの結果を取得する
- async/awaitのエラーハンドリングはtry-catchもしくはawait-catchパターンを利用する
Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!