From 56e13c5fc025b80c41ac449bbc27e386422c2cc2 Mon Sep 17 00:00:00 2001 From: azu Date: Fri, 29 Jun 2018 17:48:58 +0900 Subject: [PATCH 01/15] =?UTF-8?q?feat(async):=20=E3=82=A8=E3=83=A9?= =?UTF-8?q?=E3=83=BC=E3=83=95=E3=82=A1=E3=83=BC=E3=82=B9=E3=83=88=E3=82=B3?= =?UTF-8?q?=E3=83=BC=E3=83=AB=E3=83=90=E3=83=83=E3=82=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/basic/async/README.md | 103 ++++++++++++++++++++ source/basic/async/example/promise-catch.js | 31 ++++++ source/basic/async/example/try-catch.js | 52 +++++++--- 3 files changed, 175 insertions(+), 11 deletions(-) diff --git a/source/basic/async/README.md b/source/basic/async/README.md index 8259ca0a80..130baf1a67 100644 --- a/source/basic/async/README.md +++ b/source/basic/async/README.md @@ -239,7 +239,110 @@ console.log("この文は実行されます"); この章では主要な非同期処理と例外の扱い方としてエラーファーストコールバック、Promise、Async Functionの3つを見ていきます。 現実のコードではすべてのパターンが実用的です。そのため、非同期処理の選択肢を増やす意味でも理解することは重要です。 +## エラーファーストコールバック {#error-first-callback} + +**非同期処理の中**で例外を発生した場合に、その例外を**非同期処理の外**へ伝える方法として**エラーファーストコールバック**があります。 +エラーファーストコールバックとは、例外が発生したときはそエラーファーストコールバックのエラーをコールバック関数の最初の引数に入れて呼び出すという手法です。 +このエラーファーストコールバックはNode.jsで好んで使われ、Node.jsの標準APIにおいても非同期処理を行う関数では利用されています。 + +たとえば、Node.jsでは`fs.readFile`関数というファイルシステムからファイルをロードする非同期処理を行う関数があります。 +指定したパスのデータを読むため、ファイルが存在しない場合やアクセス権限の問題から読み取りに失敗することがあります。 +そのため、`fs.readFile`関数の第2引数にわたすコールバック関数にはエラーファーストコールバックを指定します。 + +ファイルを読み込むことに失敗した場合には、 +コールバック関数の1番目の引数にはErrorオブジェクトが渡されます。 +ファイルを読み込むことに成功した場合には、コールバック関数の1番目の引数には`null`、2番目の引数に読み込んだデータを渡します。 + +```js +fs.readFile("./example.txt", (error, data) => { + if (error) { + // 読み込み中にエラーが発生しました + } else { + // データを読み込むことができた + } +}); +``` + +実際にエラーファーストコールバック関数を扱う処理を作りながら見ていきましょう。 + +次のコードの`callTaskAsync`関数は、第1引数に非同期的に呼び出すタスクとなる関数を受け取り、第2引数にエラーファーストコールバック関数を受け取ります。 +第1引数のタスクとなる関数が失敗(例外を投げた)場合には、第2引数にエラーファーストコールバック関数にはエラーオブジェクトを渡して呼び出します。 +一方、タスクとなる関数が成功(例外を投げなかった)場合には、第2引数にエラーファーストコールバック関数には`null`とそのタスクの返り値を渡して呼び出します。 + +```js +/** + * `task`を実行して、成功なら`callback(null, タスクの返り値)`と呼び出す + * 失敗なら`callback(error)`と呼び出す + * @param {Function} task + * @param {(error: null|Error, result: *)} callback + */ +function callTaskAsync(task, callback) { + // タスクを非同期的に呼び出して、結果によってcallbackを呼び分ける + setTimeout(() => { + try { + const result = task(); + callback(null, result); + } catch (error) { + callback(error); + } + }, 10); +} + +const successTask = () => { + return "タスクが成功しました"; +}; +const failtureTask = () => { + throw new Error("タスクが失敗しました"); +}; +// sucessTaskは成功するため、`error`は`null`となり、`result`に値が入る +callTaskAsync(successTask, (error, result) => { + if (error) { + console.log(error); // 呼ばれない + } else { + console.log(result); // => "タスクが成功しました" + } +}); +// failtureTaskは失敗するため、`error`にはErrorオブジェクトが入る +callTaskAsync(successTask, (error, result) => { + if (error) { + console.log(error); // => Error: タスクが失敗しました + } else { + console.log(result); // 呼ばれない + } +}); +``` + +このように最初の引数にはエラーオブジェクトまたは`null`を入れ、それ以降の引数にデータを入れるというルール化したものをエラーファーストコールバック関数と呼びます。Node.jsでは標準APIの非同期処理においてエラーファーストコールバック関数が採用されています。 +詳しい扱い方については[ユースケース: Node.jsでCLIアプリケーション][]について紹介します。 + +コールバック関数でエラー結果を受け取る方法は他にもやり方があります。 +たとえば、成功したときに呼び出すコールバック関数と失敗したときに呼び出すコールバック関数の2つを受け取る方法があります。 +さきほどの`callTaskAsync`をその形に変更すると次のような実装になります。 + +```js +/** + * `task`を実行して、成功なら`successCallback(タスクの返り値)`と呼び出す + * 失敗なら`failureCallback(error)`と呼び出す + */ +function callTaskAsync(task, successCallback, failureCallback) { + setTimeout(() => { + try { + const result = task(); + successCallback(result); + } catch (error) { + failureCallback(error); + } + }, 10); +} +``` + +このように、**非同期処理の中**で例外を発生した場合に、その例外を**非同期処理の外**へ伝える方法はさまざまな手段が考えられます。 +エラーファーストコールバックはその形を決めた**ただの共通のルール**です。そのため、必ずしもこのパターンがすべてにおいて正しいわけではありません。 +一方で、非同期処理における例外処理のパターンを決めることのメリットとして、エラーハンドリングの共通化や書きやすさなどがあります。 + +次のセクションでは、エラーファーストコールバックでは**ただの共通のルール**であったエラーハンドリングを、**統一的なインターフェース**として扱えるようにしたPromiseを見ていきます。 [文と式]: ../statement-expression/README.md [例外処理]: ../error-try-catch/README.md [Web Worker]: https://developer.mozilla.org/ja/docs/Web/API/Web_Workers_API/Using_web_workers +[ユースケース: Node.jsでCLIアプリケーション]: ../../use-case/node-cli/README.md \ No newline at end of file diff --git a/source/basic/async/example/promise-catch.js b/source/basic/async/example/promise-catch.js index e69de29bb2..1f030478b8 100644 --- a/source/basic/async/example/promise-catch.js +++ b/source/basic/async/example/promise-catch.js @@ -0,0 +1,31 @@ +/** + * `delay * 1.5`ミリ秒以内にタイマーが呼ばれたら成功、呼ばれなかったら失敗とする関数 + * @param {Function} callback + * @param {number} delay タイマーのコールバックを呼び出すまでの時間(ミリ秒) + */ +const exactSetTimeout = (callback, delay) => { + return new Promise((resolve, reject) => { + // タイマーのコールバックが呼ばれるまでの許容時間(ミリ秒) + // `delay`に指定された時間の1.5倍まで許容する + const limitOfDelay = delay * 1.5; + const startTime = Date.now(); + setTimeout(() => { + const diffTime = Date.now() - startTime; + if (diffTime <= limitOfDelay) { + return resolve(); + } else { + return reject(new Error(`許容時間内にタイマーが呼ばれませんでした${diffTime}ミリ秒)`)); + } + }, delay); + }); +}; + +exactSetTimeout((error, message) => { + if (error) { + console.error(error); + return; + } + console.log(message); +}, 10); + + diff --git a/source/basic/async/example/try-catch.js b/source/basic/async/example/try-catch.js index dc43c4319f..4e5d7151d7 100644 --- a/source/basic/async/example/try-catch.js +++ b/source/basic/async/example/try-catch.js @@ -1,29 +1,59 @@ /** - * 指定時間内にタイマーが発火されるなら成功、そうでないなら失敗 - * @param callback + * `delay * 1.5`ミリ秒以内にタイマーが呼ばれたら成功、呼ばれなかったら失敗とする関数 + * @param {Function} callback + * @param {number} delay タイマーのコールバックを呼び出すまでの時間(ミリ秒) */ -const tryTimeout = (callback) => { - // タイマーのコールバックを呼び出すまでの時間(ミリ秒) - const delay = 10; - // タイマーのコールバックが呼ばれるまで待てる時間(ミリ秒) - const limitOfDelay = delay * 2; +const exactSetTimeout = (callback, delay) => { + // `delay`に指定された時間の1.5倍まで許容する + const limitOfDelay = delay * 1.5; const startTime = Date.now(); setTimeout(() => { const diffTime = Date.now() - startTime; if (diffTime <= limitOfDelay) { - callback(null, "許容時間内にタイマーが発火しました"); + callback(null, `許容時間内にタイマーが呼ばれました${diffTime}ミリ秒)`); } else { - callback(new Error(`許容時間よりタイマーが発火できませんでした(${diffTime}ミリ秒)`)); + callback(new Error(`許容時間内にタイマーが呼ばれませんでした${diffTime}ミリ秒)`)); } }, delay); }; -tryTimeout((error, message) => { +exactSetTimeout((error, message) => { if (error) { console.error(error); return; } console.log(message); -}); +}, 10); + +/** + * `task`を実行して、成功なら、`callback(null, タスクの返り値)`と呼び出す + * 失敗なら、`callback(error)`と呼び出す + * @param {Function} task + * @param {(error: null|Error, result: *)} callback + */ +function callTaskAsync(task, callback) { + setTimeout(() => { + try { + const result = task(); + callback(null, result); + } catch (error) { + callback(error); + } + }, 10); +} +const successTask = () => { + return "成功!"; +}; +const failtureTask = () => { + throw new Error("タスクが失敗しました"); +} + +callTaskAsync(successTask, (error, result) => { + if (error) { + console.log(error); // タスクが失敗した場合 + } else { + console.log(result); // タスクが成功した場合 + } +}); From a002f058b35fe9cb90fa30f2e0ac7148de0ac3d7 Mon Sep 17 00:00:00 2001 From: azu Date: Fri, 29 Jun 2018 17:56:39 +0900 Subject: [PATCH 02/15] fixup: lint --- source/basic/async/example/try-catch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/basic/async/example/try-catch.js b/source/basic/async/example/try-catch.js index 4e5d7151d7..ebac355e05 100644 --- a/source/basic/async/example/try-catch.js +++ b/source/basic/async/example/try-catch.js @@ -48,7 +48,7 @@ const successTask = () => { }; const failtureTask = () => { throw new Error("タスクが失敗しました"); -} +}; callTaskAsync(successTask, (error, result) => { if (error) { From 10f296e70e680a826d7d94a836735184f5839117 Mon Sep 17 00:00:00 2001 From: azu Date: Fri, 29 Jun 2018 17:57:48 +0900 Subject: [PATCH 03/15] fixup --- source/basic/async/README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/source/basic/async/README.md b/source/basic/async/README.md index 130baf1a67..55a18eb009 100644 --- a/source/basic/async/README.md +++ b/source/basic/async/README.md @@ -241,7 +241,7 @@ console.log("この文は実行されます"); ## エラーファーストコールバック {#error-first-callback} -**非同期処理の中**で例外を発生した場合に、その例外を**非同期処理の外**へ伝える方法として**エラーファーストコールバック**があります。 +**非同期処理の中**で例外を発生した場合に、その例外を**非同期処理の外**へ伝える方法の1つとして**エラーファーストコールバック**があります。 エラーファーストコールバックとは、例外が発生したときはそエラーファーストコールバックのエラーをコールバック関数の最初の引数に入れて呼び出すという手法です。 このエラーファーストコールバックはNode.jsで好んで使われ、Node.jsの標準APIにおいても非同期処理を行う関数では利用されています。 @@ -273,8 +273,6 @@ fs.readFile("./example.txt", (error, data) => { /** * `task`を実行して、成功なら`callback(null, タスクの返り値)`と呼び出す * 失敗なら`callback(error)`と呼び出す - * @param {Function} task - * @param {(error: null|Error, result: *)} callback */ function callTaskAsync(task, callback) { // タスクを非同期的に呼び出して、結果によってcallbackを呼び分ける From d32cadf2b65c415759f753f007390527d9c3553c Mon Sep 17 00:00:00 2001 From: azu Date: Fri, 29 Jun 2018 18:02:29 +0900 Subject: [PATCH 04/15] fixup: example --- source/basic/async/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/basic/async/README.md b/source/basic/async/README.md index 55a18eb009..fa144e8780 100644 --- a/source/basic/async/README.md +++ b/source/basic/async/README.md @@ -253,6 +253,7 @@ console.log("この文は実行されます"); コールバック関数の1番目の引数にはErrorオブジェクトが渡されます。 ファイルを読み込むことに成功した場合には、コールバック関数の1番目の引数には`null`、2番目の引数に読み込んだデータを渡します。 + ```js fs.readFile("./example.txt", (error, data) => { if (error) { @@ -269,6 +270,7 @@ fs.readFile("./example.txt", (error, data) => { 第1引数のタスクとなる関数が失敗(例外を投げた)場合には、第2引数にエラーファーストコールバック関数にはエラーオブジェクトを渡して呼び出します。 一方、タスクとなる関数が成功(例外を投げなかった)場合には、第2引数にエラーファーストコールバック関数には`null`とそのタスクの返り値を渡して呼び出します。 +{{book.console}} ```js /** * `task`を実行して、成功なら`callback(null, タスクの返り値)`と呼び出す From ab7396e309b2d0dfa62517748922a64fcbee9411 Mon Sep 17 00:00:00 2001 From: azu Date: Sat, 30 Jun 2018 21:51:08 +0900 Subject: [PATCH 05/15] fixup --- source/basic/async/README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/source/basic/async/README.md b/source/basic/async/README.md index fa144e8780..3ffac895e1 100644 --- a/source/basic/async/README.md +++ b/source/basic/async/README.md @@ -291,33 +291,34 @@ function callTaskAsync(task, callback) { const successTask = () => { return "タスクが成功しました"; }; -const failtureTask = () => { - throw new Error("タスクが失敗しました"); -}; // sucessTaskは成功するため、`error`は`null`となり、`result`に値が入る callTaskAsync(successTask, (error, result) => { if (error) { - console.log(error); // 呼ばれない + console.log(error); // この文は実行されません } else { console.log(result); // => "タスクが成功しました" } }); +const failtureTask = () => { + throw new Error("タスクが失敗しました"); +}; // failtureTaskは失敗するため、`error`にはErrorオブジェクトが入る -callTaskAsync(successTask, (error, result) => { +callTaskAsync(failtureTask, (error, result) => { if (error) { console.log(error); // => Error: タスクが失敗しました } else { - console.log(result); // 呼ばれない + console.log(result); // この文は実行されません } }); ``` -このように最初の引数にはエラーオブジェクトまたは`null`を入れ、それ以降の引数にデータを入れるというルール化したものをエラーファーストコールバック関数と呼びます。Node.jsでは標準APIの非同期処理においてエラーファーストコールバック関数が採用されています。 +このように最初の引数にはエラーオブジェクトまたは`null`を入れ、それ以降の引数にデータを入れるというルール化したものをエラーファーストコールバック関数と呼びます。 +Node.jsにおける標準APIの非同期処理においてエラーファーストコールバック関数が利用されています。 詳しい扱い方については[ユースケース: Node.jsでCLIアプリケーション][]について紹介します。 コールバック関数でエラー結果を受け取る方法は他にもやり方があります。 たとえば、成功したときに呼び出すコールバック関数と失敗したときに呼び出すコールバック関数の2つを受け取る方法があります。 -さきほどの`callTaskAsync`をその形に変更すると次のような実装になります。 +さきほどの`callTaskAsync`を2種類のコールバック関数を受け取る形に変更すると次のような実装になります。 ```js /** From 30ed7bd4457d875453187e783c16a8608988cbcd Mon Sep 17 00:00:00 2001 From: azu Date: Sat, 30 Jun 2018 22:12:56 +0900 Subject: [PATCH 06/15] =?UTF-8?q?fixup:=20Promise=E3=81=A8=E3=81=A4?= =?UTF-8?q?=E3=81=AA=E3=81=8C=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prh.yml | 10 +++++++--- source/basic/async/README.md | 26 ++++++++++++++------------ 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/prh.yml b/prh.yml index 6a6b138d81..4004b82b63 100644 --- a/prh.yml +++ b/prh.yml @@ -236,11 +236,12 @@ rules: - expected: HTML要素 patterns: - DOM要素 - - expected: 同期処理 + - expected: エラーファーストコールバック patterns: - - 動機処理 + - エラーファーストコールバック関数 + - # 一致とマッチ + # 一致とマッチ ## 一致は === - expected: $1一致 patterns: @@ -289,6 +290,9 @@ rules: - expected: されています patterns: - されいます + - expected: 同期処理 + patterns: + - 動機処理 # 文体 - expected: どのように diff --git a/source/basic/async/README.md b/source/basic/async/README.md index 3ffac895e1..bcbf440c81 100644 --- a/source/basic/async/README.md +++ b/source/basic/async/README.md @@ -241,16 +241,17 @@ console.log("この文は実行されます"); ## エラーファーストコールバック {#error-first-callback} -**非同期処理の中**で例外を発生した場合に、その例外を**非同期処理の外**へ伝える方法の1つとして**エラーファーストコールバック**があります。 -エラーファーストコールバックとは、例外が発生したときはそエラーファーストコールバックのエラーをコールバック関数の最初の引数に入れて呼び出すという手法です。 -このエラーファーストコールバックはNode.jsで好んで使われ、Node.jsの標準APIにおいても非同期処理を行う関数では利用されています。 +ECMAScript 2015(ES2015)で`Promise`が仕様へ入るまで、非同期処理中に発生した例外を扱う統一的な方法は存在しませんでした。 +ES2015より前までは、**エラーファーストコールバック**という非同期処理中に発生した例外を扱う方法を決めたコミュニティベースのルールが広く使われていました。 + +エラーファーストコールバックとは、発生した例外のエラーオブジェクトをコールバック関数の最初の引数にして呼び出すという手法です。 +このエラーファーストコールバックはNode.jsでは広く使われ、Node.jsの標準APIにおいても非同期処理を行う関数では利用されています。 たとえば、Node.jsでは`fs.readFile`関数というファイルシステムからファイルをロードする非同期処理を行う関数があります。 指定したパスのデータを読むため、ファイルが存在しない場合やアクセス権限の問題から読み取りに失敗することがあります。 そのため、`fs.readFile`関数の第2引数にわたすコールバック関数にはエラーファーストコールバックを指定します。 -ファイルを読み込むことに失敗した場合には、 -コールバック関数の1番目の引数にはErrorオブジェクトが渡されます。 +ファイルを読み込むことに失敗した場合には、コールバック関数の1番目の引数には`Error`オブジェクトが渡されます。 ファイルを読み込むことに成功した場合には、コールバック関数の1番目の引数には`null`、2番目の引数に読み込んだデータを渡します。 @@ -264,11 +265,11 @@ fs.readFile("./example.txt", (error, data) => { }); ``` -実際にエラーファーストコールバック関数を扱う処理を作りながら見ていきましょう。 +実際にエラーファーストコールバックを扱う処理を作りながら見ていきましょう。 -次のコードの`callTaskAsync`関数は、第1引数に非同期的に呼び出すタスクとなる関数を受け取り、第2引数にエラーファーストコールバック関数を受け取ります。 -第1引数のタスクとなる関数が失敗(例外を投げた)場合には、第2引数にエラーファーストコールバック関数にはエラーオブジェクトを渡して呼び出します。 -一方、タスクとなる関数が成功(例外を投げなかった)場合には、第2引数にエラーファーストコールバック関数には`null`とそのタスクの返り値を渡して呼び出します。 +次のコードの`callTaskAsync`関数は、第1引数に非同期的に呼び出すタスクとなる関数を受け取り、第2引数にエラーファーストコールバックを受け取ります。 +第1引数のタスクとなる関数が失敗(例外を投げた)場合には、第2引数にエラーファーストコールバックにはエラーオブジェクトを渡して呼び出します。 +一方、タスクとなる関数が成功(例外を投げなかった)場合には、第2引数にエラーファーストコールバックには`null`とそのタスクの返り値を渡して呼び出します。 {{book.console}} ```js @@ -312,8 +313,8 @@ callTaskAsync(failtureTask, (error, result) => { }); ``` -このように最初の引数にはエラーオブジェクトまたは`null`を入れ、それ以降の引数にデータを入れるというルール化したものをエラーファーストコールバック関数と呼びます。 -Node.jsにおける標準APIの非同期処理においてエラーファーストコールバック関数が利用されています。 +このように最初の引数にはエラーオブジェクトまたは`null`を入れ、それ以降の引数にデータを入れるというルール化したものをエラーファーストコールバックと呼びます。 +Node.jsにおける標準APIの非同期処理においてエラーファーストコールバックが利用されています。 詳しい扱い方については[ユースケース: Node.jsでCLIアプリケーション][]について紹介します。 コールバック関数でエラー結果を受け取る方法は他にもやり方があります。 @@ -341,7 +342,8 @@ function callTaskAsync(task, successCallback, failureCallback) { エラーファーストコールバックはその形を決めた**ただの共通のルール**です。そのため、必ずしもこのパターンがすべてにおいて正しいわけではありません。 一方で、非同期処理における例外処理のパターンを決めることのメリットとして、エラーハンドリングの共通化や書きやすさなどがあります。 -次のセクションでは、エラーファーストコールバックでは**ただの共通のルール**であったエラーハンドリングを、**統一的なインターフェース**として扱えるようにしたPromiseを見ていきます。 +エラーファーストコールバックは非同期処理におけるエラーハンドリングの**ただの共通のルール**でした。 +次のセクションでは、ES2015で導入されたPromiseという非同期処理を**統一的なインターフェース**として扱えるようにしたものを見ていきます。 [文と式]: ../statement-expression/README.md [例外処理]: ../error-try-catch/README.md From 27f0e5ad794ab08a83b38192cd7897ca901864d1 Mon Sep 17 00:00:00 2001 From: azu Date: Sat, 30 Jun 2018 22:37:22 +0900 Subject: [PATCH 07/15] fixup --- source/basic/async/README.md | 48 +++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/source/basic/async/README.md b/source/basic/async/README.md index bcbf440c81..1bf0823c01 100644 --- a/source/basic/async/README.md +++ b/source/basic/async/README.md @@ -249,7 +249,7 @@ ES2015より前までは、**エラーファーストコールバック**とい たとえば、Node.jsでは`fs.readFile`関数というファイルシステムからファイルをロードする非同期処理を行う関数があります。 指定したパスのデータを読むため、ファイルが存在しない場合やアクセス権限の問題から読み取りに失敗することがあります。 -そのため、`fs.readFile`関数の第2引数にわたすコールバック関数にはエラーファーストコールバックを指定します。 +そのため、`fs.readFile`関数の第2引数にわたすコールバック関数にはエラーファーストコールバックスタイルの関数を渡します。 ファイルを読み込むことに失敗した場合には、コールバック関数の1番目の引数には`Error`オブジェクトが渡されます。 ファイルを読み込むことに成功した場合には、コールバック関数の1番目の引数には`null`、2番目の引数に読み込んだデータを渡します。 @@ -265,11 +265,14 @@ fs.readFile("./example.txt", (error, data) => { }); ``` -実際にエラーファーストコールバックを扱う処理を作りながら見ていきましょう。 +このようなエラーファーストコールバックがNode.jsの標準APIでは採用されています。 +詳しい扱い方については[ユースケース: Node.jsでCLIアプリケーション][]にて紹介します。 -次のコードの`callTaskAsync`関数は、第1引数に非同期的に呼び出すタスクとなる関数を受け取り、第2引数にエラーファーストコールバックを受け取ります。 -第1引数のタスクとなる関数が失敗(例外を投げた)場合には、第2引数にエラーファーストコールバックにはエラーオブジェクトを渡して呼び出します。 -一方、タスクとなる関数が成功(例外を投げなかった)場合には、第2引数にエラーファーストコールバックには`null`とそのタスクの返り値を渡して呼び出します。 +実際にエラーファーストコールバックで非同期な例外処理を扱うコードを書いてみましょう。 + +次のコードの`callTaskAsync`関数は、第1引数に非同期的に呼び出すタスクとなる関数を受け取り、第2引数にエラーファーストコールバックスタイルの関数を受け取ります。 +第1引数のタスクとなる関数が失敗(例外を投げた)場合には、第2引数のコールバック関数にはエラーオブジェクトを渡して呼び出します。 +一方、タスクとなる関数が成功(例外を投げなかった)場合には、第2引数のコールバック関数には`null`とそのタスクの返り値を渡して呼び出します。 {{book.console}} ```js @@ -288,18 +291,7 @@ function callTaskAsync(task, callback) { } }, 10); } - -const successTask = () => { - return "タスクが成功しました"; -}; -// sucessTaskは成功するため、`error`は`null`となり、`result`に値が入る -callTaskAsync(successTask, (error, result) => { - if (error) { - console.log(error); // この文は実行されません - } else { - console.log(result); // => "タスクが成功しました" - } -}); +// 非同期処理が失敗する場合 const failtureTask = () => { throw new Error("タスクが失敗しました"); }; @@ -311,13 +303,23 @@ callTaskAsync(failtureTask, (error, result) => { console.log(result); // この文は実行されません } }); +// 非同期処理が成功する場合 +const successTask = () => { + return "タスクが成功しました"; +}; +// sucessTaskは成功するため、`error`は`null`となり、`result`に値が入る +callTaskAsync(successTask, (error, result) => { + if (error) { + console.log(error); // この文は実行されません + } else { + console.log(result); // => "タスクが成功しました" + } +}); ``` -このように最初の引数にはエラーオブジェクトまたは`null`を入れ、それ以降の引数にデータを入れるというルール化したものをエラーファーストコールバックと呼びます。 -Node.jsにおける標準APIの非同期処理においてエラーファーストコールバックが利用されています。 -詳しい扱い方については[ユースケース: Node.jsでCLIアプリケーション][]について紹介します。 +このように最初の引数にはエラーオブジェクトまたは`null`を入れ、それ以降の引数にデータを入れるというルール化したものを**エラーファーストコールバック**と呼びます。 -コールバック関数でエラー結果を受け取る方法は他にもやり方があります。 +非同期処理中に例外が発生して生じたエラーをコールバック関数で受け取る方法は他にもやり方があります。 たとえば、成功したときに呼び出すコールバック関数と失敗したときに呼び出すコールバック関数の2つを受け取る方法があります。 さきほどの`callTaskAsync`を2種類のコールバック関数を受け取る形に変更すると次のような実装になります。 @@ -339,8 +341,8 @@ function callTaskAsync(task, successCallback, failureCallback) { ``` このように、**非同期処理の中**で例外を発生した場合に、その例外を**非同期処理の外**へ伝える方法はさまざまな手段が考えられます。 -エラーファーストコールバックはその形を決めた**ただの共通のルール**です。そのため、必ずしもこのパターンがすべてにおいて正しいわけではありません。 -一方で、非同期処理における例外処理のパターンを決めることのメリットとして、エラーハンドリングの共通化や書きやすさなどがあります。 +エラーファーストコールバックはその形を決めた**ただの共通のルール**の1つです。そのため、エラーファーストコールバック以外の方法が使われていることも多いです。 +一方で、非同期処理における例外処理のパターンを決めることのメリットとして、エラーハンドリングの共通化できることや書きやすさなどがあります。 エラーファーストコールバックは非同期処理におけるエラーハンドリングの**ただの共通のルール**でした。 次のセクションでは、ES2015で導入されたPromiseという非同期処理を**統一的なインターフェース**として扱えるようにしたものを見ていきます。 From 2a3a4b6a330375091d867f1a57f9dd9a3365d2f8 Mon Sep 17 00:00:00 2001 From: azu Date: Sat, 30 Jun 2018 22:42:54 +0900 Subject: [PATCH 08/15] fixup --- source/basic/async/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/basic/async/README.md b/source/basic/async/README.md index 1bf0823c01..12052657bf 100644 --- a/source/basic/async/README.md +++ b/source/basic/async/README.md @@ -241,7 +241,7 @@ console.log("この文は実行されます"); ## エラーファーストコールバック {#error-first-callback} -ECMAScript 2015(ES2015)で`Promise`が仕様へ入るまで、非同期処理中に発生した例外を扱う統一的な方法は存在しませんでした。 +ECMAScript 2015(ES2015)でPromiseが仕様へ入るまで、非同期処理中に発生した例外を扱う統一的な方法は存在しませんでした。 ES2015より前までは、**エラーファーストコールバック**という非同期処理中に発生した例外を扱う方法を決めたコミュニティベースのルールが広く使われていました。 エラーファーストコールバックとは、発生した例外のエラーオブジェクトをコールバック関数の最初の引数にして呼び出すという手法です。 From 6ac7951748421ff6ab8bcb99a493fbfdcec75a1f Mon Sep 17 00:00:00 2001 From: azu Date: Sat, 30 Jun 2018 22:48:24 +0900 Subject: [PATCH 09/15] fixup --- source/basic/async/README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/source/basic/async/README.md b/source/basic/async/README.md index 12052657bf..10219e60f7 100644 --- a/source/basic/async/README.md +++ b/source/basic/async/README.md @@ -244,8 +244,12 @@ console.log("この文は実行されます"); ECMAScript 2015(ES2015)でPromiseが仕様へ入るまで、非同期処理中に発生した例外を扱う統一的な方法は存在しませんでした。 ES2015より前までは、**エラーファーストコールバック**という非同期処理中に発生した例外を扱う方法を決めたコミュニティベースのルールが広く使われていました。 -エラーファーストコールバックとは、発生した例外のエラーオブジェクトをコールバック関数の最初の引数にして呼び出すという手法です。 -このエラーファーストコールバックはNode.jsでは広く使われ、Node.jsの標準APIにおいても非同期処理を行う関数では利用されています。 +エラーファーストコールバックとは、次のような非同期処理におけるコールバック関数の呼び出し方を決めたルールです。 + +- 処理に失敗した場合は、コールバック関数の1番目の引数にエラーオブジェクトを渡して呼び出す +- 処理に成功した場合は、コールバック関数の1番目の引数には`null`を渡し、2番目以降の引数に成功時のデータを渡して呼び出す + +つまり、ひとつのコールバック関数で失敗した場合と成功した場合の両方を扱うルールとなります。 たとえば、Node.jsでは`fs.readFile`関数というファイルシステムからファイルをロードする非同期処理を行う関数があります。 指定したパスのデータを読むため、ファイルが存在しない場合やアクセス権限の問題から読み取りに失敗することがあります。 @@ -265,7 +269,7 @@ fs.readFile("./example.txt", (error, data) => { }); ``` -このようなエラーファーストコールバックがNode.jsの標準APIでは採用されています。 +このエラーファーストコールバックはNode.jsでは広く使われ、Node.jsの標準APIにおいても非同期処理を行う関数では利用されています。 詳しい扱い方については[ユースケース: Node.jsでCLIアプリケーション][]にて紹介します。 実際にエラーファーストコールバックで非同期な例外処理を扱うコードを書いてみましょう。 From 8bd42491340c8c0cca082b6037db1e2c4a6d4697 Mon Sep 17 00:00:00 2001 From: azu Date: Sat, 30 Jun 2018 22:48:41 +0900 Subject: [PATCH 10/15] =?UTF-8?q?fixup:=20=E3=81=AF=E3=82=92=E7=B5=B1?= =?UTF-8?q?=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/basic/async/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/basic/async/README.md b/source/basic/async/README.md index 10219e60f7..ec55629fe8 100644 --- a/source/basic/async/README.md +++ b/source/basic/async/README.md @@ -255,8 +255,8 @@ ES2015より前までは、**エラーファーストコールバック**とい 指定したパスのデータを読むため、ファイルが存在しない場合やアクセス権限の問題から読み取りに失敗することがあります。 そのため、`fs.readFile`関数の第2引数にわたすコールバック関数にはエラーファーストコールバックスタイルの関数を渡します。 -ファイルを読み込むことに失敗した場合には、コールバック関数の1番目の引数には`Error`オブジェクトが渡されます。 -ファイルを読み込むことに成功した場合には、コールバック関数の1番目の引数には`null`、2番目の引数に読み込んだデータを渡します。 +ファイルを読み込むことに失敗した場合は、コールバック関数の1番目の引数には`Error`オブジェクトが渡されます。 +ファイルを読み込むことに成功した場合は、コールバック関数の1番目の引数には`null`、2番目の引数に読み込んだデータを渡します。 ```js From 8265257155b71d5d0f242dd1ab4441a6c9dd5718 Mon Sep 17 00:00:00 2001 From: azu Date: Sat, 30 Jun 2018 22:50:59 +0900 Subject: [PATCH 11/15] fixup --- source/basic/async/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/basic/async/README.md b/source/basic/async/README.md index ec55629fe8..c4b1fd3a84 100644 --- a/source/basic/async/README.md +++ b/source/basic/async/README.md @@ -247,7 +247,7 @@ ES2015より前までは、**エラーファーストコールバック**とい エラーファーストコールバックとは、次のような非同期処理におけるコールバック関数の呼び出し方を決めたルールです。 - 処理に失敗した場合は、コールバック関数の1番目の引数にエラーオブジェクトを渡して呼び出す -- 処理に成功した場合は、コールバック関数の1番目の引数には`null`を渡し、2番目以降の引数に成功時のデータを渡して呼び出す +- 処理に成功した場合は、コールバック関数の1番目の引数には`null`を渡し、2番目以降の引数に成功時の結果などを渡して呼び出す つまり、ひとつのコールバック関数で失敗した場合と成功した場合の両方を扱うルールとなります。 From 03d32bbd56f492ff4fe87140dfb6af03ca014beb Mon Sep 17 00:00:00 2001 From: azu Date: Sat, 30 Jun 2018 22:51:55 +0900 Subject: [PATCH 12/15] fix link --- source/basic/async/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/basic/async/README.md b/source/basic/async/README.md index c4b1fd3a84..974c16a29b 100644 --- a/source/basic/async/README.md +++ b/source/basic/async/README.md @@ -354,4 +354,4 @@ function callTaskAsync(task, successCallback, failureCallback) { [文と式]: ../statement-expression/README.md [例外処理]: ../error-try-catch/README.md [Web Worker]: https://developer.mozilla.org/ja/docs/Web/API/Web_Workers_API/Using_web_workers -[ユースケース: Node.jsでCLIアプリケーション]: ../../use-case/node-cli/README.md \ No newline at end of file +[ユースケース: Node.jsでCLIアプリケーション]: ../../use-case/nodecli/README.md \ No newline at end of file From 35d1dfc0ddcadafa6d68ba6df732f81ac327586e Mon Sep 17 00:00:00 2001 From: azu Date: Sat, 30 Jun 2018 23:59:53 +0900 Subject: [PATCH 13/15] fixup --- source/basic/async/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/basic/async/README.md b/source/basic/async/README.md index 974c16a29b..1ac5a96c1b 100644 --- a/source/basic/async/README.md +++ b/source/basic/async/README.md @@ -329,8 +329,8 @@ callTaskAsync(successTask, (error, result) => { ```js /** - * `task`を実行して、成功なら`successCallback(タスクの返り値)`と呼び出す - * 失敗なら`failureCallback(error)`と呼び出す + * `task`を実行して、成功なら`successCallback(タスクの返り値)`を呼び出す + * 失敗なら`failureCallback(error)`を呼び出す */ function callTaskAsync(task, successCallback, failureCallback) { setTimeout(() => { @@ -346,7 +346,7 @@ function callTaskAsync(task, successCallback, failureCallback) { このように、**非同期処理の中**で例外を発生した場合に、その例外を**非同期処理の外**へ伝える方法はさまざまな手段が考えられます。 エラーファーストコールバックはその形を決めた**ただの共通のルール**の1つです。そのため、エラーファーストコールバック以外の方法が使われていることも多いです。 -一方で、非同期処理における例外処理のパターンを決めることのメリットとして、エラーハンドリングの共通化できることや書きやすさなどがあります。 +一方で、非同期処理における例外処理のルールを決めることのメリットとして、エラーハンドリングのパターン化ができることなどがあります。 エラーファーストコールバックは非同期処理におけるエラーハンドリングの**ただの共通のルール**でした。 次のセクションでは、ES2015で導入されたPromiseという非同期処理を**統一的なインターフェース**として扱えるようにしたものを見ていきます。 From d5ce4438d1fe909e41ae5954082cfca148488c01 Mon Sep 17 00:00:00 2001 From: azu Date: Sun, 1 Jul 2018 00:09:26 +0900 Subject: [PATCH 14/15] fixup --- source/basic/async/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/basic/async/README.md b/source/basic/async/README.md index 1ac5a96c1b..c2f1ed7079 100644 --- a/source/basic/async/README.md +++ b/source/basic/async/README.md @@ -321,7 +321,7 @@ callTaskAsync(successTask, (error, result) => { }); ``` -このように最初の引数にはエラーオブジェクトまたは`null`を入れ、それ以降の引数にデータを入れるというルール化したものを**エラーファーストコールバック**と呼びます。 +このようにコールバック関数の1番目の引数にはエラーオブジェクトまたは`null`を入れ、それ以降の引数にデータを渡すというルール化したものを**エラーファーストコールバック**と呼びます。 非同期処理中に例外が発生して生じたエラーをコールバック関数で受け取る方法は他にもやり方があります。 たとえば、成功したときに呼び出すコールバック関数と失敗したときに呼び出すコールバック関数の2つを受け取る方法があります。 From 0445cff290c13ac38c75604c4335c94f58c9e030 Mon Sep 17 00:00:00 2001 From: azu Date: Sun, 1 Jul 2018 07:53:03 +0900 Subject: [PATCH 15/15] fixup: fix typo --- source/basic/async/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/basic/async/README.md b/source/basic/async/README.md index c2f1ed7079..7fb5b24835 100644 --- a/source/basic/async/README.md +++ b/source/basic/async/README.md @@ -344,7 +344,7 @@ function callTaskAsync(task, successCallback, failureCallback) { } ``` -このように、**非同期処理の中**で例外を発生した場合に、その例外を**非同期処理の外**へ伝える方法はさまざまな手段が考えられます。 +このように**非同期処理の中**で例外が発生した場合に、その例外を**非同期処理の外**へ伝える方法はさまざまな手段が考えられます。 エラーファーストコールバックはその形を決めた**ただの共通のルール**の1つです。そのため、エラーファーストコールバック以外の方法が使われていることも多いです。 一方で、非同期処理における例外処理のルールを決めることのメリットとして、エラーハンドリングのパターン化ができることなどがあります。