async function 宣言は、 AsyncFunction オブジェクトを返す 非同期関数 を定義します。非同期関数は非同期でイベントループを介して実行され、暗黙的にPromiseを返します。なおコードのシンタックス及び構造は通常の同期関数と非常に似たものになります。
async function 式 を使用して async 関数を定義することもできます。
このインタラクティブデモのソースは GitHub のリポジトリに格納されています。インタラクティブデモプロジェクトに協力したい場合は、 https://github.com/mdn/interactive-examples をクローンしてプルリクエストを送ってください。
構文
async function name([param[, param[, ... param]]]) {
statements
}
引数
name- 関数名。
param- 関数に渡す引数名。
statements- 関数の本体を構成するステートメント。
戻り値
関数の中に含まれる、コードを実行する非同期関数を表す AsyncFunction オブジェクトを、resolved Promise の値と共に返します。rejectedの場合は補足されない例外を非同期関数から投げます。
説明
async 関数は、 await 式を含むことができます。 await 式は、async 関数の実行を一時停止し、 Promise の解決を待ちます。そして async 関数の実行を再開し、解決された値を返します。
キーワード await は、async 宣言された関数の中でのみ有効です. async 関数の外で使用した場合は SyntaxErrorとなります。
async/await 関数の目的は、 promise を同期的に使用する動作を簡素化し、 Promise のグループに対して何らかの動作を実行することです。 Promise が構造化コールバックに似ているのと同様に、 async/await はジェネレータと promise を組み合わせたものに似ています。
例
シンプルな例
var resolveAfter2Seconds = function() {
console.log("starting slow promise");
return new Promise(resolve => {
setTimeout(function() {
resolve("slow");
console.log("slow promise is done");
}, 2000);
});
};
var resolveAfter1Second = function() {
console.log("starting fast promise");
return new Promise(resolve => {
setTimeout(function() {
resolve("fast");
console.log("fast promise is done");
}, 1000);
});
};
var sequentialStart = async function() {
console.log('==SEQUENTIAL START==');
// 1. ここは即時実行される
const slow = await resolveAfter2Seconds();
console.log(slow); // 2. ここは 1. の2秒後に実行される
const fast = await resolveAfter1Second();
console.log(fast); // 3. ここは 1. の3秒後に実行される
}
var concurrentStart = async function() {
console.log('==CONCURRENT START with await==');
const slow = resolveAfter2Seconds(); // 即時実行
const fast = resolveAfter1Second(); // 即時実行
// 1. ここは即時実行される
console.log(await slow); // 2. ここは 1. の2秒後に実行される
console.log(await fast); // 3. ここは 1. の2秒後(2.の直後)に実行される
}
var concurrentPromise = function() {
console.log('==CONCURRENT START with Promise.all==');
return Promise.all([resolveAfter2Seconds(), resolveAfter1Second()]).then((messages) => {
console.log(messages[0]); // slow
console.log(messages[1]); // fast
});
}
var parallel = async function() {
console.log('==PARALLEL with await Promise.all==');
// 2つの jobs を並列に実行し両方が完了するのを待つ
await Promise.all([
(async()=>console.log(await resolveAfter2Seconds()))(),
(async()=>console.log(await resolveAfter1Second()))()
]);
}
// この関数はエラーハンドリングをしていません。後述の注意書きを参照してください。
var parallelPromise = function() {
console.log('==PARALLEL with Promise.then==');
resolveAfter2Seconds().then((message)=>console.log(message));
resolveAfter1Second().then((message)=>console.log(message));
}
sequentialStart(); // 2秒後に "slow" をログ出力し、その1秒後に "fast" をログ出力する
// 見やすくするため setTimeout で直前の処理が終わるのを待つ
setTimeout(concurrentStart, 4000); // 2秒後に "slow" と "fast" をログ出力する
// 直前の処理を待つ
setTimeout(concurrentPromise, 7000); // concurrentStart と同様
// 直前の処理を待つ
setTimeout(parallel, 10000); // 本当に並列処理となるため1秒後に "fast" とログ出力し、その1秒後に "slow" とログ出力する
// 直前の処理を待つ
setTimeout(parallelPromise, 13000); // parallel と同様
await と並列性
sequentialStart では、最初の await のために実行が 2 秒間待機し、 2 つ目の await のためにさらに 1 秒間待機します。 2 つ目のタイマーは最初のタイマーが起動している間は作成されません。コードは 3 秒後に終了します。
concurrentStart では、両方のタイマーが作成され、両方とも await される、すなわち待機させられます。タイマーは同時に実行されているため、 3 秒後ではなく 2 秒後に、すなわち最も遅いタイマーにあわせて終了します。
しかし、 await の呼び出しは依然として逐次処理であり、これは 2 つ目の await が 1 つ目の終了まで待つことを意味します。このケースでは、最も速いタイマーが最も遅いタイマーのあとに処理されることになります。
もし複数の処理を完全に並列に実行したい場合は、上記コード中の parallel のように await Promise.all([job1(), job2()]) を使わなければなりません。
async/await vs Promise#then およびエラーハンドリング
多くの async 関数は Promise を用いて通常の関数として書くことができます。しかし async 関数はエラーハンドリングにおいてほんの僅かばかりエラーを起こしにくいです。
concurrentStartとconcurrentPromiseのどちらも関数としては同値です。concurrentStartでは、awaitされたいずれかの関数呼び出しが失敗すれば、例外は自動的にキャッチされ、async 関数の実行が中断され、暗黙的にリターンされる Promise を経由してエラーが呼び出し元へ伝えられます。
同じことが Promise のケースでも起こり、関数は、関数の完了をとらえて戻ってくる Promise の面倒を見なければなりません。これは concurrentPromise ではPromise.all([]).then() が返す promise を return することを意味します。実は、この例の前のバージョンはこれをやり忘れていました!
しかしながら async 関数も誤ってエラーを飲み込んでしまうことがあります。
上記の parallel という async 関数を例にしてみましょう。もしこれが Promise.all([]) 呼び出しの結果を await (もしくは return) しなければ、任意のエラーは伝わりません。
parallelPromise の例は簡潔に見えるものの、エラーをまったくハンドルしていません!同じことをするには、やはり return Promise.all[()] が必要になります。
promise チェーンをasync function で 書き換える
promise を返す API は promise チェーンで解決され、関数を複数の部品に分割できます。次のコードを想定してください。:
function getProcessedData(url) {
return downloadData(url) // returns a promise
.catch(e => {
return downloadFallbackData(url) // returns a promise
})
.then(v => {
return processDataInWorker(v); // returns a promise
});
}
次のように 1 つの async 関数に書き直すことができます。
async function getProcessedData(url) {
let v;
try {
v = await downloadData(url);
} catch(e) {
v = await downloadFallbackData(url);
}
return processDataInWorker(v);
}
上記の例では、 return ステートメント上に await ステートメントがないことに注目してください。なぜなら、async function の戻り値は暗黙的に Promise.resolve でラップされているからです。
仕様
| 仕様 | ステータス | コメント |
|---|---|---|
| ECMAScript (ECMA-262) async function の定義 |
現行の標準 | ES2017 における最初の定義。 |
| ECMAScript 2017 (ECMA-262) async function の定義 |
標準 |
ブラウザー実装状況
| デスクトップ | モバイル | サーバー | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
async function | Chrome 完全対応 55 | Edge 完全対応 15 | Firefox 完全対応 52 | IE 未対応 なし | Opera 完全対応 42 | Safari 完全対応 10.1 | WebView Android 完全対応 55 | Chrome Android 完全対応 55 | Firefox Android 完全対応 52 | Opera Android 完全対応 42 | Safari iOS 完全対応 10.3 | Samsung Internet Android 完全対応 6.0 | nodejs
完全対応
7.6.0
|
凡例
- 完全対応
- 完全対応
- 未対応
- 未対応
- ユーザーが明示的にこの機能を有効にしなければなりません。
- ユーザーが明示的にこの機能を有効にしなければなりません。