From 33058f2b292b3a581333bdfb21b8f671898c5060 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:40:17 -0500 Subject: initial commit --- .../reference/statements/async_function/index.html | 261 +++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 files/ja/web/javascript/reference/statements/async_function/index.html (limited to 'files/ja/web/javascript/reference/statements/async_function') diff --git a/files/ja/web/javascript/reference/statements/async_function/index.html b/files/ja/web/javascript/reference/statements/async_function/index.html new file mode 100644 index 0000000000..4c4032c50d --- /dev/null +++ b/files/ja/web/javascript/reference/statements/async_function/index.html @@ -0,0 +1,261 @@ +--- +title: 非同期関数 +slug: Web/JavaScript/Reference/Statements/async_function +tags: + - Example + - Function + - JavaScript + - Language feature + - Statement + - 文 + - 言語機能 + - 関数 +translation_of: Web/JavaScript/Reference/Statements/async_function +--- +
{{jsSidebar("Statements")}}
+ +

async function 宣言は、 非同期関数 — {{jsxref("Global_Objects/AsyncFunction","AsyncFunction")}} オブジェクトである関数を定義します。非同期関数はイベントループを介して他のコードとは別に実行され、結果として暗黙の {{jsxref("Promise")}} を返します。ただし、非同期関数を使用したコードの構文および構造は、通常の同期関数と似たものになります。

+ +
+

{{jsxref("Operators/async_function", "async function 式", "", 1)}} を使用して非同期関数を定義することもできます。

+
+ +
{{EmbedInteractiveExample("pages/js/statement-async.html", "taller")}}
+ + + +

構文

+ +
async function name([param[, param[, ...param]]]) {
+   statements
+}
+
+ +

引数

+ +
+
name
+
関数名。
+
param
+
関数に渡す引数名。
+
statements
+
関数の本体を構成する文。
+
+ +

返値

+ +

{{jsxref("Promise")}} で、非同期関数から返される値で解決するか、または非同期関数内の捕捉されなかった例外で拒否します。

+ +

解説

+ +

非同期関数は、 {{jsxref("Operators/await", "await")}} 式を含むことができます。これは非同期関数の実行を一時停止し、 Promise の解決を待ちます。そして非同期関数の実行を再開し、解決された値を返します。

+ +

キーワード await は、非同期関数の中でのみ有効です。非同期関数の外で使用した場合は {{jsxref("SyntaxError")}} となります。

+ +

非同期関数が一時停止している間、呼び出し側の関数は実行が続きます (非同期関数から返される暗黙の Promise を受け取ります)。

+ +
+

async/await の目的は、 Promise を同期的に使用する動作を簡素化し、 Promise のグループに対して何らかの動作を実行することです。 Promise が構造化コールバックに似ているのと同様に、 async/await はジェネレーターと Promise を組み合わせたものに似ています。

+
+ +

+ +

シンプルな例

+ +
function resolveAfter2Seconds() {
+  console.log("starting slow promise")
+  return new Promise(resolve => {
+    setTimeout(function() {
+      resolve("slow")
+      console.log("slow promise is done")
+    }, 2000)
+  })
+}
+
+function resolveAfter1Second() {
+  console.log("starting fast promise")
+  return new Promise(resolve => {
+    setTimeout(function() {
+      resolve("fast")
+      console.log("fast promise is done")
+    }, 1000)
+  })
+}
+
+async function sequentialStart() {
+  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秒後に実行される
+}
+
+async function concurrentStart() {
+  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.の直後)に実行される
+}
+
+function concurrentPromise() {
+  console.log('==CONCURRENT START with Promise.all==')
+  return Promise.all([resolveAfter2Seconds(), resolveAfter1Second()]).then((messages) => {
+    console.log(messages[0]) // slow
+    console.log(messages[1]) // fast
+  })
+}
+
+async function parallel() {
+  console.log('==PARALLEL with await Promise.all==')
+
+  // 2つの jobs を並列に実行し両方が完了するのを待つ
+  await Promise.all([
+      (async()=>console.log(await resolveAfter2Seconds()))(),
+      (async()=>console.log(await resolveAfter1Second()))()
+  ])
+}
+
+// この関数はエラーハンドリングをしていません。後述の注意書きを参照してください。
+function parallelPromise() {
+  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 と Promise.then およびエラー処理

+ +

多くの非同期関数は Promise を用いて通常の関数として書くことができます。しかし async 関数はエラー処理において少し簡単です。

+ +

concurrentStartconcurrentPromiseのどちらも関数としては同値です。

+ + + +

しかしながら非同期関数も誤ってエラーを飲み込んでしまうことがあります。

+ +

上記の parallel という非同期関数を例にしてみましょう。もしこれが 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 の返値は暗黙的に {{jsxref("Promise.resolve")}} でラップされているからです。

+ +
+

return await promiseValue と return promiseValue

+ +

返値が {{jsxref("Promise.resolve")}} で暗黙にラッピングされるとはいえ、 return await promiseValuereturn promiseValue と機能的に等価である訳ではありません。

+ +

上記のコードを以下のように書き直したと想像してください。これは processDataInWorker がエラーで拒否した場合に null を返します。

+ +
async function getProcessedData(url) {
+  let v
+  try {
+    v = await downloadData(url)
+  } catch(e) {
+    v = await downloadFallbackData(url)
+  }
+  try {
+    return await processDataInWorker(v)  // Note the `return await` vs. just `return`
+  } catch (e) {
+    return null
+  }
+}
+
+ +

return processDataInWorker(v) と記述すると、 processDataInWorker(v) が拒否した場合に null に解決されるのではなく、関数が返した {{jsxref("Promise")}} が拒否されてしまいます。

+ +

これは、 return foo;return await foo; の微妙な違いを強調しています。 - return foo はすぐに foo を返し、 foo が拒否する Promise であっても例外を発生させません。 return await foo は、それが Promise であれば foo が解決するか拒否するかを待ち、拒否した場合は返す前に例外を発生させます。

+
+ +

仕様書

+ + + + + + + + + + + + +
仕様書
{{SpecName('ESDraft', '#sec-async-function-definitions', 'async function')}}
+ +

ブラウザーの互換性

+ +
+ + +

{{Compat("javascript.statements.async_function")}}

+
+ +

関連情報

+ + -- cgit v1.2.3-54-g00ecf