💬

非同期処理(Promise)とは何か? async awaitも

2022/08/26に公開

JSの基礎に立ち帰ると、意外と漏れてしまっている記法がたくさんあることに気づく、、
かなりの数使ってきた非同期処理について書いていく

同期処理

変数に値を格納したり、コールバック関数なしの処理を実行する場合

const name = "Tanaka"
console.log(`Hello, ${name} san.`)

非同期処理

関数の中で実行されるコールバック関数など
今回の例だと、setTimeout関数のコールバック

const name = "Tanaka"
setTimeout((name) => {
	console.log(`Hello, ${name} san.`)
}, 2000)

2秒後に、第1引数のコールバック関数が実行される
このようにプログラムは基本、上から下に流れるように実行されるが、
コードの順番通りに実行されない処理のことを非同期処理という。

では非同期処理が終わったタイミングで、次の処理を繋げて実行したい場合はどうしたらいいの...?

Promiseを使ってみる

そこでPromiseというオブジェクトが使われるらしい。
Promiseをインスタンス化して、引数にコールバック関数を書く。
その引数にresolveとrejectがコールバック関数として渡ってくる。

処理の中に、resolveを実行すると、チェーンで繋がったthenメソッドの引数内のコールバック関数が実行される。
この時、resolveの引数に値を入れておくと、thenで値が渡ってくるようになる。

...説明が難しい。
コードにするとこんな感じ
コンソールログでHelloした後にresolve関数で、次の処理に続くイメージ

new Promise((resolve, reject) => {
	const name = "Tanaka"
	setTimeout((name) => {
	console.log(`Hello, ${name} san.`)
	resolve(name)
}).then((speaker) => {
	console.log(`Hi, ${speaker} san.`)
	return speaker
}).then((speaker) => {
	console.log(`Hi, ${speaker} san.`)
})

thenは複数チェーン化することも可能(順々に実行される)。
2回目のthenメソッドで引数をもらう場合は、戻り値を設定する。

reject関数は、非同期処理書いたことある人だとわかりやすい。
例外処理でエラーが返されるときにcatchメソッドが実行されるイメージ。
reject関数の引数に値をセットしておくと、catchメソッドで渡ってくる。

new Promise((resolve, reject) => {
	const name = "Tanaka"
	setTimeout((name) => {
	console.log(`Hello, ${name} san.`)
	reject(name)
}).then((speaker) => {
	// ここは今回実行されない
	console.log(`Hello, ${speaker} san.`)
}).catch((speaker) => {
	console.log(`Bye, ${speaker} san.`)
})
Hello, Tanaka san.
Bye, Tanaka san.

でも毎回つなげないといけないの、、?
そこでasync、awaitが登場。

asyc、awaitの処理

グローバル変数に初期値0を代入。
関数内にPromiseでコールバック関数を記述する。が、インスタンスの外でconsole.logを実行すると、numは0のまま。

const init = () => {
    new Promise((resolve, reject) => {
        setTimeout(() => {
            num = 100;
            resolve(a)
        }, 2000);
    })
    console.log(num)
}
0

そこで、関数内の処理を順番に実行するときは、async, awaitを使う。
resolveの処理が終わるまで、Promise外の処理は実行されない。
これを読む方は、すでにWebAPIをガンガン叩いていそうなので、コードだけ載せる。

const init = async () => {
    await new Promise((resolve, reject) => {
        setTimeout(() => {
            num = 100;
            resolve(num)
        }, 2000);
    })
    console.log(num)
}

計画通り。

100

ちなみにインスタンス化したPromiseを変数に格納する場合、resolveの引数の値が格納される。

const init = async () => {
    await const result = new Promise((resolve, reject) => {
        setTimeout(() => {
            num = 100;
            resolve(num)
        }, 2000);
    })
    console.log(result)
}

結果は同じ。

100

まとめ

  • 処理の中で、順番に呼ばれない処理を非同期処理という(かなりざっくりとしたイメージだが)。
  • 順番に実行したい場合、Promise内のチェーンで繋げて実装。
  • Promise以外の処理も待ってほしい場合、asyncとawaitをあてる。

何となく書けるだけで、Promiseの概念を理解していなかったので良い学びになりました。
ReactとTypescriptの理解が浅い部分も随時記事化していきます。

Discussion