非同期処理 (Promise) - JavaScript入門

f:id:utouto97:20210704221944p:plain

JavaScriptの非同期処理

JavaScriptでは、非同期処理を行うことができます。
非同期処理とは、その処理の完了を待たずにほかの処理が実行される処理のことです。
タイマーや外部APIとの通信のように、待ちが発生してしまう処理を非同期で処理することで、処理が止まってしまわないようにします。

イメージを載せておきます。

f:id:utouto97:20210708215524p:plainf:id:utouto97:20210708220050p:plain

そして、非同期処理の完了後に何か処理をしたい場合があると思います。
(例えば、APIをコールしてその結果を使いたい場合)

そのようなときに、利用できる方法がJavaScriptでは、三種類あります。
コールバック関数、Promise、async/awaitです。
コールバックについては↓

utouto97.hatenablog.com

今回はコールバック関数を利用する場合の課題点とPromiseについてまとめていきます。

非同期処理とコールバック関数

非同期処理にコールバック関数を使う場合の課題点をみてみます。

まず、順々に処理したい非同期処理があった場合にネストが深く深くなってしまうことです。

例えば、1秒後に1、2秒後に2、3秒後に3と表示するプログラムを考えます。
setTimeoutとコールバック関数を使って書くと次のようになります。

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

setTimeoutのコールバック関数でsetTimeoutを呼び出していて、ネストが深くなっています。
処理がもっと続くと、さらにネストが深くなることが予想できます。

また、コールバック関数を使う場合の別の課題として、例外処理が難しいことが挙げられます。

次のような,プログラムだと例外をキャッチできません。

try {
    setTimeout(() => {
        throw new Error("非同期的なエラー");
    }, 10);
} catch (error) {
    // 非同期エラーはキャッチできないため、この行は実行されません
}
console.log("この行は実行されます");

JavaScriptにはPromiseがある

先に述べたような理由があり、コールバック関数で非同期処理をすべて記述するのは非常に難しいことがわかります。

JavaScriptにはPromiseというオブジェクトがあり、Promiseを使うことで非同期処理が書きやすくなります。

function setPromiseTimer(ms) {
  return new Promise(function(resolve, reject) { 
      setTimeout(function() { 
          resolve() 
      }, ms)
  })
} 

setPromiseTimer(1000)
.then(function() {
  console.log('1')
  return setPromiseTimer(1000)
}).then(function() {
  console.log('2')
  return setPromiseTimer(1000)
}).then(function() {
  console.log('3')
})

Promiseは一つのオブジェクトです。
Promiseを新規に作成する場合、コンストラクタの引数には二つの引数をとる関数を渡します。
この二つの引数はresolveとrejectで、これらはメソッドです。
コンストラクタの引数に渡す関数では、非同期で行いたい処理を書き、処理の最後にresolveを呼び出します。
また、途中で例外を発生させたい場合には、rejectを呼び出します。

Promiseオブジェクト自体は、thenメソッドとcatchメソッドを持っており、それぞれresolve、rejectが呼ばれた場合に、実行されるコールバック関数を指定します。 つまり、非同期処理が完了した場合には、resolveが呼ばれ、その後thenに渡したコールバック関数が呼び出されます。
逆に、非同期処理中に例外が発生した場合は、rejectが呼ばれ、その後catchに渡したコールバック関数が呼び出されます。

Promiseはthenでつなげて書くことができるため、ネストがどんどん深くなることはありません。
また、catchによる例外処理も容易にできます。

終わり