Getting Started

2020/02/18

JavaScript 非同期処理

どうも、こんにちは!JellyWareです。

Getting Started!! Riiiver tutorialの第7弾となります。

このシリーズは、Riiiverやプログラミングを学び、初心者の方でもEco-Drive Riiiverを活用したオリジナルアプリを理解して作れるようになることを目指します。

第7弾では、非同期処理について学びます。

中級者向けとなりますが、内容を絞って解説していきます。わかりやすいように「パスタの調理」をプログラミングに置き換えて話を進めます。


目次


モジュール

今回からモジュールというものを活用していきます。モジュールとは、簡単に例えると「部品」です。

プログラミングも工作のように「部品」を組み合わせることでプログラムを作成します。前回学んだ関数や変数が「部品」にあたります。

まずは、こちらのファイルをダウンロードし、以下の画面(glitchで作成したプロジェクト)にドラッグアンドドロップします。
(glitchについては第6弾をご参照ください。)

すると、自動的にglitch上にファイルがアップロードされます。

このファイルが「モジュール」です※1

次に、モジュールの使い方を説明します。
モジュールを使用するには、require()という関数で読み込む必要があります。これ以降のコードは、server.jsに記入してください。

// アップロードしたモジュールの場合
const 変数 = require("モジュールのパス");

// インストールしたモジュールの場合
const 変数 = require("モジュール名");

読み込んだモジュールは、このように変数に代入して活用します。

今回は「アップロードしたモジュール」のため、モジュールのパスを指定して次のように読み込みます。「パス」とは、ファイルが存在する場所のことです。

例えば、今回であれば次のように読み込んでいます。

const cook = require("./cook.js");

./をつけてcook.jsのパスを引数としてrequire()に渡しています※2

また、読み込んだモジュール内の関数は次のように使います。

cook.example();

このように.で変数と関数を繋げる必要があります※3。実行するとこちらはモジュールの例です。と表示されます。

使用モジュール

今回使うモジュール(cook.js)には、同期処理のsync()と非同期処理のasync()の2つの関数があります。(後の同期処理・非同期処理の説明で使用します。)

どちらの関数も2つ引数が必要で、順に「調理するもの」,「調理にかかる時間(秒)」を渡します。

関数を実行すると、「調理にかかる時間」の秒数が経過すると、「調理するもの」の料理名と「完成!」の文字列が結合されて戻り値として返されます。

例えば、「調理するもの:パスタ」,「調理にかかる時間(秒):5」で同期処理の場合、次のように使用します。

const cook = require("./cook.js");

// パスタを作るのに5秒かかる
const pasta = cook.sync("パスタ", 5);
console.log(pasta);

上記コードを実行すると、5秒後にパスタ完成!と表示されます。

この2つの関数を用いて、同期処理と非同期処理の違いを確認していきます。


同期処理と非同期処理

具体的な解説に入る前に、「パスタの調理」を例にして「同期処理」「非同期処理」について説明します。

次の2つの手順を行うものとします。

  1. ソースを作る
  2. パスタを茹でる

それぞれ次のような流れになります。

同期処理の場合、1つずつ順に行っていきます。そのため、「ソースを作る」間に「パスタを茹でる」ことができません。

一方、非同期処理の場合だと「ヒト」と「コンロ」で作業を分けて行うことができます。

これが「同期処理」と「非同期処理」の違いで、結果として非同期処理の方が効率よく作業を終えられます。

以降、実際にコードを実行しながら確認していきます。


同期処理

同期処理とは、コードの記述順にプログラムが実行されることを言います。

また、前の処理が完了してから次の処理の実行に移ります。

同期処理を行うsync()を実行してみましょう。

const cook = require("./cook.js"); console.log("調理開始"); // パスタ作成 const pasta = cook.sync("パスタ", 5); console.log(pasta);

用意したモジュールを活用します。1行目のrequire()で読み込み、6行目でモジュール内のsync()関数を実行しています。

調理開始
パスタ完成!

実行すると調理開始が表示され、その5秒後にパスタ完成!と表示されます。


非同期処理

一方で非同期処理は、記述順にプログラムが実行されません。

次に、非同期処理を行うasync()を実行してみましょう。

const cook = require("./cook.js"); console.log("調理開始"); // パスタ作成 const pasta = cook.async("パスタ", 5); console.log(pasta);

6行目のsync()async()に変更しました。

調理開始
Promise { <pending> }

処理の内容は同じですが、非同期処理のため出力結果が異なります。

pendingとは、文字通りの意味で「保留中」となります。(ビジネス用語のペンディングと同じニュアンスです。)

6行目のasync()よりも先に7行目のconsole.log(pasta)が実行され、「処理が保留中」ということを表しています。

await

では、非同期処理が終わるまで次の処理に移らないようにするにはどうすれば良いのでしょうか?

awaitを使用すれば解決します。ただし、awaitを使うだけではエラーが起きます。

以下のコードを実行してみましょう。

const cook = require("./cook.js"); console.log("調理開始"); // パスタ作成 const pasta = await cook.async("パスタ", 5); console.log(pasta);

6行目のcook.async()の前にawaitを追加しました。

SyntaxError: await is only valid in async function

すると、このようなエラーが発生します。

そうです、async functionの中でしかawaitは活用できません。

async function

さきほど説明したawaitを使うにはasync functionを活用する必要があります。

活用方法は以下の通りです。アロー関数の( )の前にasyncをつけるだけです。

const doAsyncFunction = async () => {
  // 処理
}

それでは、先ほどエラーが起きたコードを修正して改めて実行してみましょう。

const cook = require("./cook.js"); console.log("調理開始"); const cookPasta = async () => { const pasta = await cook.async("パスタ", 5); console.log(pasta); } // パスタ作成 cookPasta();

awaitasync function内で使用するように修正しました。

調理開始
パスタ完成!

同期処理と同じように表示できました。

応用例

それでは最後に、「同期処理」「非同期処理」を使って「パスタとソースの調理」を行います。処理にかかる時間に注目です。

まずは同期処理から実行しましょう。

const cook = require("./cook.js"); console.log("調理開始"); // ソース作成 const source = cook.sync("ソース", 3); console.log(source); // パスタ作成 const pasta = cook.sync("パスタ", 5); console.log(pasta);

同期処理のsync()を使用しています。「ソース」→「パスタ」の順番に作成します。

調理開始
ソース完成!
パスタ完成!

上から順に、調理開始、その3秒後にソース完成!、その5秒後にパスタ完成!と表示されます。

次に非同期処理を実行しましょう。

const cook = require("./cook.js"); console.log("調理開始"); const cookSource = async () => { const source = await cook.async("ソース", 3); console.log(source); } // ソース作成 cookSource(); const cookPasta = async () => { const pasta = await cook.async("パスタ", 5); console.log(pasta); } // パスタ作成 cookPasta();

async functionawaitを活用して非同期処理のasync()を使用しています。
「ソース」と「パスタ」を同時進行で作成します。

調理開始
ソース完成!
パスタ完成!

同期処理とは異なり、調理開始、3秒後にソース完成!、その2秒後にパスタ完成!と表示されます。
効率よく処理を行えているのが確認できます。

また、同時進行のため「ソース」と「パスタ」の実行する順番を変えても表示内容は同じです。気になる方は試してみましょう!

注釈

※1 今回は手動でモジュールを用意しましたが、ターミナルやコマンドプロンプトでコマンドを使うこともできます(これをインストールと言います)。次回、そのやり方を解説します。

※2 ファイルのパスは./で表します。詳しくは、「ディレクトリ パス 書き方」で検索。

※3 モジュールによって扱い方は異なります。モジュールを扱う際には、適宜使い方を調べてから活用しましょう。「JavaScript モジュール名 使い方」などで検索。


以上で、非同期処理についての説明は終わりです。

次回行うAPI通信でもモジュールと非同期処理を活用します。

非同期処理の扱い方を忘れてしまった際には、本コンテンツを復習していただければと思います。

ご覧くださいましてありがとうございました。