JavaScriptの非同期処理をアロー関数を使わない方法で考える


目次(Table of Content)

アロー関数を使わずにPromiseを使った非同期処理を理解したい

JavaScriptのPromiseを使った非同期処理について調べている中で、アロー関数を使って説明しているサイトが多いなと思いました。

私はいつも関数リテラルを定義する時に、匿名関数のスタイルで記述をしています。
アロー関数については知識としては入っていますが、最初に身に付けたスタイルのため理解する上では慣れ親しんだスタイルの方が良いなと思い、無名関数のスタイルで非同期処理の流れを整理したいと思いました。

対象として想定している読者

・JavaScriptでPromiseを使った非同期処理について勉強中の人
・普段、匿名関数(無名関数)のスタイルでコードを書いている人
・まずはシンプルに非同期処理の流れを知りたい人

ファイル構成

使うファイルはシンプルに2つです。
HTML内の<script>タグと、外部読み込みの.jsファイルを使います。

index.html
└ js/async.js

非同期処理の呼び出し側(index.html)

ブラウザで読み込むためのHTMLファイルです。

<!DOCTYPE html>
<script>
  //外部読み込みの非同期処理で値を変化させるために先に変数を宣言します。
  var result = `init`; 
</script>
<script type="text/javascript" src="js/async.js"></script>
<body>
  <script>
    var param = 1 ; // 失敗させる時は1以外に変更
    callAsync(param); //外部.jsファイルで定義した非同期処理を呼び出す
  </script>
</body>
</html>

呼び出される非同期処理(async.js)

実行される非同期処理を記述しています。
これでもかというくらいコメントで説明を記述してみました。

console.log(`async.js START`);
console.log(`result is ${result} from async.js`);

//具体的な処理を行う関数
function asyncProcess(received_param){

  //成功時に実行される関数:resolve , 失敗時に実行される関数:reject
  return new Promise(function(resolve,reject){
                       console.log(`asyncProcess START`);
                       //実行する非同期処理のブロック      
                       if(received_param === 1){  //簡略化した成否判定
                         resolve('Success');  //成功時はresolve関数を実行
                       }else{
                         reject('Fail');//失敗はreject関数を実行
                       }
                       console.log(`asyncProcess END`);
                      } //--function
             );//-Promise
}

//非同期処理を呼び出すための関数
function callAsync(param){
  console.log(`callAsync START`);

  //asyncProcess(param)はPromiseオブジェクトを受け取っている
  asyncProcess(param).then(   //Promise(param).then(--実行処理--)
    //成功時:resolveが実行された時の関数
    function(received_resolve_response){
             console.log(received_resolve_response); //今回は"Success"
             console.log(`RESPONSE START`);
             result =`Response is ${received_resolve_response}`; 
             console.log(`RESPONSE END , ${result} ` );
    },
   //失敗時:rejectが実行された時の関数
    function (received_reject_error){ 
          console.log(received_reject_error);//今回は"Fail"
          console.log(`ERROR START`);
          result =`Error is ${received_reject_error}`; 
          console.log(`ERROR END , ${result}`);
    }
  );
  console.log(`callAsync END`);
}
console.log(`async.js END`);

ブラウザで実行してみよう

index.htmlをダブルクリックしてブラウザから起動をしてみましょう。
おすすめはGoogleChromeです。
F12キーを押すことでデベロッパーツールを開くことができます。
[console]タブをクリックして、F5を押して再読み込みしましょう。

そうすると、写真のような順番でコンソールに表示されます。

コンソールの順番を基に実行準を考えると下記の流れとなります。

callAsync(param)が実行される
callAsync内部でasyncProcess(param)が実行される。
asyncProcessで処理を行う。成功したらresolve関数、失敗したらreject関数を実行する。
callAsyncの返却された関数と引数を基に処理を行う。

基本は成功時と失敗時の実行関数の2つを定義

呼び出す際の関数と具体的な処理関数の両者とも、成功時と失敗時の2つの関数を定義する必要があるようです。

処理部分とPromiseの部分を分けて考える

非同期処理では関数を返却することが多くなるため、どの段階の関数かを意識していくことが重要になると考えられます。
まずは、具体的な非同期の処理部分とPromiseオブジェクト内の処理化を考えてみると分かりやすくなるのではないでしょうか。

async/awaitで非同期処理の終了の待ち合わせをする

非同期処理というからには、基本的には他の処理と分かれた並行処理となります。つまり、処理の順番が順不同となります。1
でも、どうしても非同期処理の結果を使いたいという場面はありますよね。
そんな時はasyncawaitというものを使うと非同期処理の結果を待つことができるようです。

index.htmlを<body>部分をこのように書き換えます

<body>
 <script>
    async function sync(param){
      callAsync(param);
      console.log(`Side index.html sync END`);
    } 
    var param = 1 ; // 失敗させる時は1以外に変更
    sync(param);
  </script>
</body>
</html>

これを実行するとこのようにコンソールの内容が変わります。

外部.jsファイルの非同期処理を呼び出した後すぐにindex.html次の行のログを実行していますね。

await設定

asyncはawaitと組み合わせることで効果を発揮します。
index.htmlを書き換えます。

非同期処理呼び出しの callAsyncの前にawaitを加えます。

  <script>
    async function sync(param){
      console.log(`Side index.html sync START`);
      await callAsync(param);  //awaitを挿入
      console.log(`Side index.html sync END`);
    } 
    var param = 1 ; // 失敗させる時は1以外に変更
    sync(param);
  </script>

ブラウザをF5でリロードしてコンソールを確認してみましょう。

callAsyncで呼び出した非同期処理の終了を待ってからindex.html側の次の行を実行していることが確認できました。

async/awaitのまとめ

まず、非同期処理の待ち合わせをするためにはfunctionの前にasyncの宣言を行う必要があります。
asyncで設定された関数の内部で非同期処理の結果の待ち合わせをする必要があるステートメントの前にawaitを記述することで、処理待機をします。

今回はawaitの部分は簡略化したものでしたが、非同期処理結果を受け取る場合には下記のような記述になります。

var res = await callAsync(param);

このコードは callAsyncの結果を変数resに
代入しています。

最後に

非同期処理は結構複雑な事をしている印象ですが、内容としては実務で避けて通ることのできない処理かと思います。
今回は、私のような「アロー関数とは??」「非同期処理とは??」というような状態の駆け出しエンジニアのためになればと思い本記事を作成しました。

※記載しているコードですが、関数や引数のブロックを強調するためにフォーマットを基本スタイルからかなり崩しています。インデントのつけかたなど実務でやると確実に怒られると思うので参考にはしないでください。
確実にレビューで怒られます(笑)

一応こちらのソースについてはGithubで公開しておきますので、ご参考ください。
https://github.com/koh1project/analyze_async


1↩並行処理と似た言葉で並列処理というものがあります。並列処理はマルチスレッドで負荷を分散させる処理となります。JavaScriptは基本的にはシングルスレッドで実行される並行処理となりますので単語に注意した方が良さそうです。