あっくんブログ Written by Akihiro Tsuji

JavaScriptによる非同期処理

JavaScript プログラミング

こんにちはあっくんです。僕の肌荒れが最高潮に達してきました。もともとのアレルギー体質と日々の不眠とかから来てるかも知れません。健康って本当に大切ですね。明日、また病院に行ってみます。

それでは,JavaScriptによる非同期処理をやっていきましょー!!

JavaScriptによる非同期処理

非同期処理を書くための仕組みの一つであるPromiseがあります。asynccとawitという非同期処理を短く方法もあります。

非同期処理とは

非同期処理とは時間のかかる処理は終了をまたずに先に進め、処理が終わったタイミングで関数を実行するという方式です。
JavaScriptは基本的にシングルスレッドのプログラミング言語です。処理は1つずつ終わるのを街、次の処理へと進んで行きます。このシングルスレッドの方式は、通信などの待機時間がある処理で問題になます。待ち時間が終わるまでにすべての処理が止まり、操作不能になるからです。

Image from Gyazo

例、処理1,2,5,6と進んだあとに3,4を実行します。setTimeout()は一定時間待機したあとに引数の関数を実行する命令です。

Image from Gyazo
<html> <script> // メインの処理の流れ。 console.log("処理1"); console.log("処理2"); // 100ミリ秒後に実行する処理 setTimeout(() => { console.log("処理2"); console.log("処理3"); }, 100); // メインのしょりの流れ console.log("処理5"); console.log("処理6"); </script> </html>
Code language: HTML, XML (xml)

非同期処理はコールバック関数を使います。非同期の処理が複数にになることもあります。その場合、コールバック関数のネストは深くなり、プログラムはみにくくなります。
 こうした問題を解決するために、Promiseという仕組みがあります。

例、非同期処理が入れ子になったプログラム。

Image from Gyazo
<html> <script> console.log("処理1"); // 100ミリ秒待って実行 setTimeout(() => { console.log("処理A"); // 100ミリ秒待って実行 setTimeout(() => { console.log("処理B"); // 100ミリ秒待って実行 setTimeout(() => { console.log("処理C"); // 100ミリ秒待って実行 setTimeout(() => { console.log("処理D"); // 100ミリ秒待って実行 setTimeout(() => { console.log("処理E"); }, 100); }, 100); }, 100); }, 100); }, 100); console.log("処理2"); </script> </html>
Code language: HTML, XML (xml)

Promise

Promiseとは、約束です。Promiseオブジェクトは、後で処理することを約束して、先にすすむためのものです。
Promiseオブジェクトは、new Promise(関数)と、コールバック関数を引数にしてインスタンスを作ります。コールバック関数は、resolve(解決)とreject(拒否)という2つの関数を引数に取ります。そして、時間がかかる処理をしたあと、正常に終了すればresolve()関数を、異常に終了すればreject()関数を実行します。

構文

new Promise((resolve, reject) => { 時間のかかる処理 正常に終了したとき終了したときしたとき()を実行実行 異常に終了したとき終了したときしたとき()を実行実行 })
Code language: JavaScript (javascript)

Promiseオブジェクトには処理が終わったあとに、続きの処理をするためのメソッドがあります。最も一般的なメソッドはthen(関数)です。then()のコールバック関数は、resolve()やreject()を実行すると呼び出されます。resolve(),reject()に引数を設定したときは、コールバック関数の値を受け取れます。

構文

new Promise((resolve, reject) => { 時間のかかる処理 正常に終了したとき終了したときしたとき(dateA)を実行実行 異常に終了したとき終了したときしたとき(dataB)を実行実行 }) .then(dataA => { resolve()を実行した時の処理した時の処理時の処理 resolve()の引数を }, dataB => { reject()を実行した時の処理した時の処理時の処理(書かなくてもよい reject()の引数を });
Code language: JavaScript (javascript)

.then()は、再びPromiseオブジェクトを返します。そのため、.then()のあとに、さらに.thenを書くことができます。

構文

new Promise((resolve, reject) => { }) .then(dataA => { }) .then(dataA => { }) .then(dataA => { });
Code language: JavaScript (javascript)

.then()のコールバック関数の戻り値としてPromiseオブジェクトを書いたときは、その処理の終了を待って、次の.then()を実行します。戻り値がPromiseオブジェクトでないときは待機せず、すぐに次の制御が移ります。

構文

new Promise((resolveA, reject) => { 時間のかかる処理 正常に終了したときは()を実行実行 }) .then(data => { resolveA()を実行したときの処理 変数P数P= new Promise((resolveB, reject) => { 時間のかかる処理 正常に終了したときは()を実行実行 }) return 変数P数P; }) .then(data => { resolveB()を実行したときの処理 変数P数P= new Promise((resolveC, reject) => { 時間のかかる処理 正常に終了したときは()を実行実行 }) return 変数P数P; })
Code language: JavaScript (javascript)

例、Promise。

Image from Gyazo
<html> <script> console.log("処理1"); new Promise((resolve, reject) => { // 100ミリ秒待って実行 setTimeout(() => { console.log("処理A"); resolve(); }, 100); }) .then((d) => { return new Promise((resolve, reject) => { // 100ミリ秒待って実行 setTimeout(() => { console.log("処理B"); resolve(); }, 100); }); }) .then((d) => { return new Promise((resolve, reject) => { // 100ミリ秒待って実行 setTimeout(() => { console.log("処理C"); resolve(); }, 100); }); }) .then((d) => { return new Promise((resolve, reject) => { // 100ミリ秒待って実行 setTimeout(() => { console.log("処理D"); resolve(); }, 100); }); }) .then((d) => { return new Promise((resolve, reject) => { // 100ミリ秒待って実行 setTimeout(() => { console.log("処理E"); resolve(); }, 100); }); }); console.log("処理2"); </script> </html>
Code language: HTML, XML (xml)

例、Promise。非同期処理を関数を使って見やすくします。

Image from Gyazo
<html> <script> // 待機用の関数 function wait(msg) { // Promiseオブジェクトを戻り値にする return new Promise((resolve, reject) => { // 100ミリ秒待って実行 setTimeout(() => { console.log(msg); resolve(); }, 100); }); } console.log("処理1"); wait("処理A") .then((d) => { return wait("処理B"); }) .then((d) => { return wait("処理C"); }) .then((d) => { return wait("処理D"); }) .then((d) => { return wait("処理E"); }); console.log("処理2"); </script> </html>
Code language: HTML, XML (xml)

Promiseのインスタンスメソッド

Promiseオブジェクトのインスタンスメソッドは.3種類あります。

Promiseオブジェクトのインスタンスメソッド

.then(f1, f2)
resolve()のときは関数f1, reject()のときは関数f2を実行。

.catch(f1)
reject()のときは関数f1を実行。

.finally(f1)
resolve()でもreject()でも関数f1を実行。

.then()は(第1引数のみを使い)正常時の処理、                                 .catch()は異常時の処理、                                           .finally()は両方で行う最終処理で使い分けます。

例、Promise。reject()が実行されたら、間にあるresolve()は一挙に飛ばされます。

Image from Gyazo
<html> <script> // 待機用の関数 function wait(msg, isOk = true) { // Promiseオブジェクトを戻り値にする。 return new Promise((resolve, reject) => { // 100ミリ秒待って実行 setTimeout(() => { console.log(msg); if (isOk) { // isOk我trueなら解決 resolve(); } else { // isOkがfalseなら拒否 reject(); } }, 100); }); } wait("処理A", false) //ここでrejectに .then( (d) => { return wait("処理B 成功"); }, (d) => { return wait("処理B 失敗"); } ) .then((d) => { return wait("処理C 成功", false); }) .then((d) => { return wait("処理D 成功"); }) .then((d) => { return wait("処理E 成功"); }) .catch((d) => { return wait("処理F 失敗"); }) .finally((d) => { return wait("処理G 終了"); }); </script> </html>
Code language: HTML, XML (xml)

例、Promise。throw文をつかって、処理C, 処理Dを飛ばします。

Image from Gyazo
<html> <script> // 待機用の関数 function wait(msg) { // Promiseオブジェクトを戻り値にする return new Promise((resolve, reject) => { // 100ミリ秒待って実行 setTimeout(() => { console.log(msg); resolve(); }, 100); }); } wait("処理A") .then((d) => { throw new Error("Oh!"); //例外を起こす return wait("処理B"); }) .then((d) => { return wait("処理C"); }) .then((d) => { return wait("処理D"); }) .catch((d) => { return wait("処理E"); }) .finally((d) => { return wait("処理F"); }); </script> </html>
Code language: HTML, XML (xml)

Promiseの静的メソッド

Promiseの静的メソッドは複数のPromiseをまとめて扱えます。                                      並列の処理です。

Promiseオブジェクトの静的メソッド

.all(a)
Promiseの配列aが全て解決すれば、解決とみなして結果の配列をあとの処理に送る。
1つでも拒否されれば拒否と見なし、結果をあとの処理に送ります。

.any(a)
Promiseの配列aが1つでも解決すれば、解決と見なして結果をあとの処理に送る。全て拒否すれば拒否と見なし、結果をあとの処理に贈ります。

.allSettled(a)
Promiseの配列aの処理が全て終われば、解決とみなしてすべての結果を配列にして送ります。

.race(a)
Promiseの配列aの処理が1つでも終われば次の処理に移行します。その1つの処理が解決なら解決と見なし、拒否なら拒否とみなします。

例、Promise.all()。                                                         全てが解決するか、拒否が起きれば、先に進みます。

Image from Gyazo
<html> <body> <script> // 待機用の関数 function p(tm, isResolve = true) { // Promiseオブジェクトを戻り値にする return new Promise((resolve, reject) => { // tmミリ秒待って実行 setTimeout(() => { // コンソールに情報を出力 console.log(tm, isResolve ? "resolve" : "reject"); if (isResolve) { // isResolveがtrueなら解決 resolve(`${tm}:ok`); } else { // isResolveがfalseなら拒否 reject(`${tm}:err`); } }, tm); }); } // Promise.all()その1 setTimeout(() => { console.log("--- Promise.all() その1 ---"); // Promiseオブジェクトの配列を作成 const arr = [p(1, true), p(2, true), p(3, true)]; // Promise.all()の処理 Promise.all(arr) .then((data) => { // 成功時 console.log("--> then :", data); }) .catch((data) => { // 失敗 console.log("--> catch :", data); }); }, 0); // Promise.all()その2 setTimeout(() => { console.log("--- Promise.all() その2 ---"); // Promiseオブジェクトの配列を作成 const arr = [p(1, true), p(2, false), p(3, true)]; // Promise.all()の処理 Promise.all(arr) .then((data) => { // 成功時 console.log("--> then :", data); }) .catch((data) => { // 失敗時 console.log("--> catch :", data); }); }, 100); </script> </body> </html>
Code language: HTML, XML (xml)

例、Promise.any()。                                                       全てが拒否されるか、解決が起きれば先に進ます。

Image from Gyazo
<html> <script> // 待機用の関数 function p(tm, isResolve = true) { // Promiseオブジェクトを戻り値にする return new Promise((resolve, reject) => { // tmミリ秒待って実行 setTimeout(() => { // コンソールに情報を出力 console.log(tm, isResolve ? "resolve" : "reject"); if (isResolve) { // isResolveがtrueなら解決 resolve(`${tm}:ok`); } else { // isResolveがfalseなら拒否 reject(`${tm}:err`); } }, tm); }); } // Promise.any()その1 setTimeout(() => { console.log("--- Promise.any() その1 ---"); // Promiseオブジェクトの配列を作成 const arr = [p(1, false), p(2, true), p(3, false)]; Promise.any(arr) .then((data) => { // 成功時 console.log("--> then :", data); }) .catch((data) => { // 失敗時 console.log("--> catch :", data); }); }, 0); // Promise.any()その2 setTimeout(() => { console.log("--- Promise.any() その2 ---"); // Promise.any()の処理 const arr = [p(1, false), p(2, false), p(3, false)]; // Promise.any()の処理 Promise.any(arr) .then((data) => { // 成功時 console.log("--> then :", data); }) .catch((data) => { // 失敗時 console.log("--> catch:", data); }); }, 100); </script> </html>
Code language: HTML, XML (xml)

例、Promise.allSettled()。                                                      全ての処理が終われば、先に進みます。全ての結果を配列で受け取ります。

Image from Gyazo
<html> <script> // 待機用の関数 function p(tm, isResolve = true) { // Promiseオブジェクトを戻り値にする return new Promise((resolve, reject) => { // tmミリ秒待って実行 setTimeout(() => { // コンソールに出力 console.log(tm, isResolve ? "resolve" : "reject"); if (isResolve) { // isResolveがtrueなら解決 resolve(`${tm}:ok`); } else { // isResolveがfalseなら拒否 reject(`${tm}:err`); } }, tm); }); } // Promiseオブジェクトの配列を作成 const arr = [p(1, false), p(2, true), p(3, false)]; // Promise.allSettled()の処理 Promise.allSettled(arr).then((data) => { console.log("--> then :", data); }); </script> </html>
Code language: HTML, XML (xml)

例、 Promise.race()。                                                       最初の1つの処理が終われば、先に進みます。解決でも拒否でも構いません。

Image from Gyazo
<html> <script> // 待機用の関数 function p(tm, isResolve = true) { // Promiseオブジェクトを戻り値にする return new Promise((resolve, reject) => { // tmミリ秒待って実行 setTimeout(() => { // コンソールに出力 console.log(tm, isResolve ? "resolve" : "reject"); if (isResolve) { // isResolveがtrueなら解決 resolve(`${tm}:ok`); } else { // isResolveがfalseなら拒否 reject(`${tm}:err`); } }, tm); }); } // Promise.race()その1 setTimeout(() => { console.log("--- Promise.race() その1 ---"); // Promiseオブジェクトの配列を作成 const arr = [p(1, true), p(2, false), p(3, true)]; Promise.race(arr) .then((data) => { // 成功時 console.log("--> then : ", data); }) .catch((data) => { // 失敗時 console.log("--> catch :", data); }); }, 0); // Promise.race()その2 setTimeout(() => { console.log("--- Promise.race() その2 ---"); // Promiseオブジェクトの配列を作成 const arr = [p(1, false), p(2, true), p(3, false)]; // Promise.race()の処理 Promise.race(arr) .then((data) => { // 成功時 console.log("--> then :", data); }) .catch((data) => { // 失敗時 console.log("--> catch :", data); }); }, 100); </script> </html>
Code language: HTML, XML (xml)

asyncとawait

asyncは非同期という意味です。                                                  awaitは待つという意味です。
asyncとawaitを使った書き方でシンプルに非同期処理ができます。
async functionと書くことで、その関数は非同期処理を扱う関数になります。async function内では、非同期の関数の前にawaitをつけることで、Promiseの処理を待ってからプログラムを進めます。awaitをつけた関数の戻り値は、resolve()の引数になります。

構文

async function 関数名数名引数) { 処理 await 非同期の関数期の関数() 変数= await 非同期の関数期の関数() //非同期の関数内で、resolve(v)を実行したなら、vが変数に入る。 処理 }
Code language: JavaScript (javascript)

例、async await。

Image from Gyazo
<html> <script> // 待機用の関数 function wait(msg, isResolve = true) { // Promiseオブジェクトを戻り値にする return new Promise((resolve, reject) => { // 100ミリ秒待って実行 setTimeout(() => { if (isResolve) { if (isResolve) { // isResolveがtrueなら解決 resolve(msg); } else { // isResolveがfalseなら拒否 reject(`error (${msg}`); } } }, 100); }); } // async実験用の関数 async function exec() { console.log("--- 処理開始 ---"); console.log(await wait("処理A")); console.log(await wait("処理B")); console.log("処理途中"); const c = await wait("処理C"); const d = await wait("処理D"); console.log(c); console.log(d); console.log("--- 処理終了 ---"); } // 処理の開始 console.log("--> 処理1"); console.log("--> exec()", exec()); console.log("--> 処理2"); </script> </html>
Code language: HTML, XML (xml)

例、awaitの処理をtry catch文で加工と、reject()をcatchで捕まえられます。

Image from Gyazo
<html> <script> // 待機用の関数 function wait(msg, isResolve = true) { // Promiseオブジェクトを戻り値にする return new Promise((resolve, reject) => { // 100ミリ秒待って実行 setTimeout(() => { if (isResolve) { // isResolveがtrueなら解決 resolve(msg); } else { // isResolveがfalseなら拒否 reject(`error (${msg})`); } }, 100); }); } // async実験用の関数 async function exec() { console.log("--- 処理開始 ---"); console.log(await wait("処理A")); console.log(await wait("処理B")); // try catch文 try { // 例外が発生するかも知れない処理 console.log(await wait("処理C", false)); console.log(await wait("処理D")); } catch (e) { // 例外発生時の処理 console.log("例外発生", e); } console.log("--- 処理終了 ---"); } // 処理の開始 exec(); </script> </html>
Code language: HTML, XML (xml)

読んでいただいて、ありがとうございました!JavaScript完全入門は2021年、2月発売です。