【JavaScript】 Promiseと非同期処理を理解する

image

Version

  • Node.js v16.16.0

はじめに

非同期処理がなかなか理解できなかったので、コードと一緒に理解できるようにする。

同期処理と非同期処理の処理の順番

以下のコードを走らせるとどのような結果になるか。nodeで実行してみる。

setTimeout(() => {console.log(1)},1000)
console.log(2);
setTimeout(() => {console.log(3)},0)
console.log(4);
setTimeout(() => {console.log(5)},1000)
console.log(6);
setTimeout(() => {
    console.log(7);
    setTimeout(() => {
        console.log(8);       
    },1000)
},2000)

結果

2
4
6
3
1
5
7
8

表示順は2,4,6,3がすぐに表示され、その1秒後に1,5が表示され、その1秒後に7が表示され、その1秒後に8が表示されます。
同期処理と非同期処理は、まず同期処理が優先して処理されます。その処理が終わった後に非同期処理が実行されます。console.log(1)console.log(5)は同時に動きます。このあたり自分は1秒ずつずれて動くと勘違いしてました。
これをずらして動かすためには、 setTimeout()関数の中にsetTime()関数を入れ込む(callbackする)必要があります。7,8でcallbackをさせています。
*top画面のイメージを参考にしてください。

callbackを使って、カウントダウンをする

上のcallbackを使って、カウントダウンするコードを書くと以下のようになります。

setTimeout(() => {
  console.log(3);
  setTimeout(() => {
    console.log(2);
    setTimeout(() => {
      console.log(1);
    }, 1000);
  }, 1000);
}, 1000);

結果

3
2
1

これは、可読性が悪くコードの管理も難しいということでコールバック地獄とかよく呼ばれています。 こうしたコールバック地獄を避けるためにPromiseがあります。

Promiseを使って、カウントダウンをする

まずはコードを書きます。結果は同じです。

new Promise((resolve) => {
  setTimeout(() => {
    console.log(3);
    resolve();
  }, 1000);
})
  .then(() => {
    return new Promise((resolve) => {
      setTimeout(() => {
        console.log(2);
        resolve();
      }, 1000);
    });
  })
  .then(() => {
    return new Promise((resolve) => {
      setTimeout(() => {
        console.log(1);
        resolve();
      }, 1000);
    });
  });
 

Promiseオブジェクトは第1引数にresolve、第2引数にrejectを指定でき、resolve()させるとthen以下の関数が実行され、reject()させるとcatch以下の関数を実行させることができます。これにより非同期処理の可読性が高まり、またthencatchにより複雑な処理も可能になりました。
上記のコードの場合、resolve()により、then以下で独立してPromiseが動いています。
前のコードより複雑化したように思うかもしれませんが、メインテナンスはやりやすくなったのではと思います。

async/await を使って、カウントダウンをする

最後にasync/awaitを使って書き直します。

func = async () => {
  // 関数に対してasyncと宣言する => この関数は非同期関数
  await log(3);
  await log(2);
  await log(1); // 関数の呼び出しの前にawait => Promiseの結果がくるまで待つ
};

log = (num) => {
  // Promiseを返す関数
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log(num);
      resolve();
    }, 1000);
  });
};

func();

functionの前にasyncをつけると、非同期処理を実行できる関数を定義できる。
awaitasyncで定義された関数の中で使う。 log関数は、setTime()を使った待機処理関数をPromiseでラップしたもので、resolve()で結果を返す。 これをawaitの後で、実行するとPromiseでthenを使って実行させたことをthenなしで実行させることができるようになりました。

最後に

async/awaitは、コードはシンプルになるが、裏でPromiseが動いているので、そこを理解しないといけないなと思いました。
いろいろなコードを書きながら非同期処理の理解を深めていきたい。

参考

https://qiita.com/cheez921/items/41b744e4e002b966391a
https://knowledge.sakura.ad.jp/24890/
https://rightcode.co.jp/blog/information-technology/javascript-async-await
小学生でもわかるasync/await/Promise入門【JavaScript講座】

お知らせ

11月5日開催のアプリ開発講座の参加者募集中!!

11月5日開催のアプリ開発講座の参加者募集中!!

11月5日にアプリ開発講座を開催します!会場は岐阜県美濃加茂市のコワーキングスペース「こやぁね」です。興味のある方は是非ご参加ください!

Read More
可茂IT塾ではFlutterインターンを募集しています!

可茂IT塾ではFlutterインターンを募集しています!

可茂IT塾ではFlutterインターンを募集しています!可茂IT塾のエンジニアの判断で、一定以上のスキルをを習得した方には有給でのインターンも受け入れています。

Read More

お知らせ

11月5日開催のアプリ開発講座の参加者募集中!!

11月5日開催のアプリ開発講座の参加者募集中!!

11月5日にアプリ開発講座を開催します!会場は岐阜県美濃加茂市のコワーキングスペース「こやぁね」です。興味のある方は是非ご参加ください!

Read More
可茂IT塾ではFlutterインターンを募集しています!

可茂IT塾ではFlutterインターンを募集しています!

可茂IT塾ではFlutterインターンを募集しています!可茂IT塾のエンジニアの判断で、一定以上のスキルをを習得した方には有給でのインターンも受け入れています。

Read More