async function

async function 宣言は、 AsyncFunction オブジェクトを返す 非同期関数 を定義します。非同期関数は非同期でイベントループを介して実行され、暗黙的にPromiseを返します。なおコードのシンタックス及び構造は通常の同期関数と非常に似たものになります。

async function 式 を使用して async 関数を定義することもできます。

構文

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. Execution gets here almost instantly
  const slow = await resolveAfter2Seconds();
  console.log(slow); // 2. this runs 2 seconds after 1.

  const fast = await resolveAfter1Second();
  console.log(fast); // 3. this runs 3 seconds after 1.
}

var concurrentStart = async function() {
  console.log('==CONCURRENT START with await==');
  const slow = resolveAfter2Seconds(); // starts timer immediately
  const fast = resolveAfter1Second(); // starts timer immediately

  // 1. Execution gets here almost instantly
  console.log(await slow); // 2. this runs 2 seconds after 1.
  console.log(await fast); // 3. this runs 2 seconds after 1., immediately after 2., since fast is already resolved
}

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==');
  
  // Start 2 "jobs" in parallel and wait for both of them to complete
  await Promise.all([
      (async()=>console.log(await resolveAfter2Seconds()))(),
      (async()=>console.log(await resolveAfter1Second()))()
  ]);
}

// This function does not handle errors. See warning below!
var parallelPromise = function() {
  console.log('==PARALLEL with Promise.then==');
  resolveAfter2Seconds().then((message)=>console.log(message));
  resolveAfter1Second().then((message)=>console.log(message));
}

sequentialStart(); // after 2 seconds, logs "slow", then after 1 more second, "fast"

// wait above to finish
setTimeout(concurrentStart, 4000); // after 2 seconds, logs "slow" and then "fast"

// wait again
setTimeout(concurrentPromise, 7000); // same as concurrentStart

// wait again
setTimeout(parallel, 10000); // truly parallel: after 1 second, logs "fast", then after 1 more second, "slow"

// wait again
setTimeout(parallelPromise, 13000); // same as 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 関数はエラーハンドリングにおいてほんの僅かばかりエラーを起こしにくいです。

concurrentStartconcurrentPromiseのどちらも関数としては同値です。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 Latest Draft (ECMA-262)
async function の定義
ドラフト ES2017 における最初の定義。
ECMAScript 2017 (ECMA-262)
async function の定義
標準

ブラウザー実装状況

Update compatibility data on GitHub
デスクトップモバイルサーバー
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewAndroid 版 ChromeAndroid 版 FirefoxAndroid 版 OperaiOSのSafariSamsung InternetNode.js
async functionChrome 完全対応 55Edge 完全対応 ありFirefox 完全対応 52IE 未対応 なしOpera 完全対応 42Safari 完全対応 10.1WebView Android 完全対応 ありChrome Android 完全対応 55Firefox Android 完全対応 52Opera Android 完全対応 42Safari iOS 完全対応 10.1Samsung Internet Android 完全対応 6.0nodejs 完全対応 7.6.0
完全対応 7.6.0
完全対応 7.0.0
無効
無効 From version 7.0.0: this feature is behind the --harmony runtime flag.

凡例

完全対応  
完全対応
未対応  
未対応
ユーザーが明示的にこの機能を有効にしなければなりません。
ユーザーが明示的にこの機能を有効にしなければなりません。

関連情報