非同期処理(Promise)とは何か? async awaitも
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