メインコンテンツまでスキップ

非同期処理

プログラミングの実行として同期処理と非同期処理と呼ばれるものがあります。

まず同期処理とはこれまでやってきたような処理です。 ある一つ(1行でも良い)の処理を実行しているとき次の処理を実行することはなく、処理が終わって初めて次の処理が実行されます。

function main() {
console.log("good morning"); // この処理は同期処理なので終わるまで次に進まない
console.log("good night");
}
main()

対して、ある処理をした時にその処理の完了を待たずに次の処理へ進んでいくようなものを非同期処理と呼び、非同期の関数を非同期関数などと呼びます。

非同期関数の例としては、setTimeoutという関数があります。これは第一引数に渡した関数を、第二引数に渡したミリ秒後に実行する関数です。

function main() {
setTimeout(() => { // この処理は非同期のため、実行完了を待たずに次の処理に進む
console.log("good morning");
})
console.log("good night"); // 上の処理を待たずに実行
}
main()

なぜ非同期処理?

ではなぜこのようなものがあるのでしょうか?...と言っても難しくなりそうなので逆になかったらどうなるか、で説明したいと思います。

まずは通信速度がちょー遅い世界でGoogleMapを開いてみましょう。(GoogleChromeを前提として説明します)

  1. まずGoogleMapをブラウザで開きます。
  2. 次にF12キーを押してブラウザのデベロッパーツールを開きます。
  3. デベロッパーツールの右上あたりにある点三つを押し、「その他のツール」の中の「ネットワーク状態」を開きます。
  4. ネットワーク スロット リングの項目を「高速3G」に変更(低速3Gでもいいが遅すぎるので)
  5. ページをリロード

しばらくそのまま操作してみてください。

次に通信速度がちょー遅くて非同期処理のない世界でGoogle Mapを開いてみる...ことはできないので想像してみましょう。 どうなるか考えて、思いついたら教えてください。

非同期関数

JavaScriptには非同期を簡単に扱うために非同期関数(asynchronous function)と呼ばれるものがあります。 それは以下のように宣言することができます。

async function 関数名(引数名) {
// 処理内容
}

// アロー関数でも大丈夫
const 関数名 = async (引数名) {
// 処理内容
}

なんだかこれまでみてきた関数の宣言と同じように見えますが、唯一違いとしてasyncがくっついていますね。

では試しに非同期関数を使ってみましょう。

...Promiseって誰でしょうか?
我々はおそらく11という結果を期待したはずですが、なんか知らない奴が返ってきました。

Promise

最初の例でsetTimeOutという関数を実行した時、プログラムはsetTimeOutの完了を待たずに次の処理を実行しました。 しかしこの表現は正確ではなく、setTimeOutなどの非同期関数はPromiseと呼ばれる特殊なオブジェクトを返します。 これにより、setTimeOut関数自体は完了しているため次の処理へと進むことができるのです。

「???じゃあsetTimeOutに渡した関数はどうなっちゃったの?」
「???結局Promiseって何者なの?」

これを説明するとおそらくみんなも僕も頭がおかしくなるので割愛します。

大事なのは、非同期関数の呼び出し結果はPromiseであり、たとえただの足し算であってもその結果が返ってくることはないということです。

await

では、私たちはどうやって非同期関数の本当の結果を受け取ったらいいのでしょうか?
今のところaddAsync関数はPromiseとかいうよくわからないモノを返すただのゴミでしかありません。

addAsync関数がゴミではなくなる方法はいくつかありますが、ここでは一番理解しやすいであろうawaitというものについて説明します。

まずはawaitを使ったプログラムを見てみましょう。

やりました!確かに11という結果が取れています!

このように、非同期関数の呼び出しにawaitをつけると(正確には非同期関数の結果であるPromiseにawaitをつけると)、 そこでPromiseが実際の結果へと解決されるまで待つことができます。

しかし若干様子がおかしいですね。先ほどまで普通にconsole.log()をしていたのに、今回はasync functionの中で実行しています。 感のいい人は気づいたかもしれませんが、awaitasync functionの中でしか使うことができません。 ただしそのことにだけ注意すれば、非同期関数も普段と同じ感覚でプログラムすることができます。