むやみに async にしないほうがいい
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 0
◆コールバック関数を受け取って呼び出すところで async 関数も扱えるようにするため await すると
◆ → 同期的な通常の関数の場合でも await のところで非同期処理になって他の処理が先に行われる
◆ 「await 0;console.log(1)」 でも 1 を表示する前に他の処理が行われる
◆ 連続呼び出ししたときに一連の処理が同期的に終わらないといけない場合は 非同期化されると困る
◆ そんなときはコールバック関数を受け取る関数は非同期かどうかで処理をわけるといいかも
◆ → 同期的な通常の関数の場合でも await のところで非同期処理になって他の処理が先に行われる
◆ 「await 0;console.log(1)」 でも 1 を表示する前に他の処理が行われる
◆ 連続呼び出ししたときに一連の処理が同期的に終わらないといけない場合は 非同期化されると困る
◆ そんなときはコールバック関数を受け取る関数は非同期かどうかで処理をわけるといいかも
とりあえず async でいいや と思ってたのですがなんでも async にするのはやめたほうがいいです
例えばある関数を呼び出したときに A の処理が実行されその後に B の処理が実行される とします
同期的に 2 回連続で実行すると 関数内部が全て同期的な処理の場合は
という順で処理されます
A と B の間に非同期処理が入ると
となります
ここまでは当たり前で難しいことでもないです
同期的な処理しかしていなくても 内部でこういう関数が通されている場合があります
cb に渡す関数は async 関数ではなく同期的な関数だと 使う側はネットワーク通したり非同期になる部分がどこにもない と思うかもですが await があると同期的な関数でも Promise をかぶせて非同期処理にされます
await のところでいったん他の処理が入ります
fn1 関数は 受け取る cb 関数が async 関数の可能性もあるので まとめて await するという作りになってます
fn1 が async 関数なので普通にこれを呼び出してるなら非同期が入るとわかるかもしれませんが これがリスナのような返り値を使わない 登録しておいて呼び出されるような関数になっていると特に分かりづらいです
fn1 の返り値は使われず fn2 を実行した結果 B が呼び出されるような場合なら fn1 が async 関数であっても問題ないので cb の処理を分岐して
にすれば cb が非同期関数でないなら fn2 の呼び出しまで同期的に行えます
もし fn1 の返り値を使うのなら fn1 を async 関数にすると cb が同期関数であっても fn1 の返り値が Promise になるのでそこで非同期になってしまいます
この場合は fn1 を通常の関数にして cb が非同期関数のときには then/catch で Promise 制御する必要があります
isAsyncFunction という関数は標準のものではないので こういう感じで用意する必要があります
例えばある関数を呼び出したときに A の処理が実行されその後に B の処理が実行される とします
同期的に 2 回連続で実行すると 関数内部が全て同期的な処理の場合は
ABAB
という順で処理されます
A と B の間に非同期処理が入ると
AABB
となります
ここまでは当たり前で難しいことでもないです
同期的な処理しかしていなくても 内部でこういう関数が通されている場合があります
async function fn1(cb){
const value = await cb({option})
fn2(value)
}
cb に渡す関数は async 関数ではなく同期的な関数だと 使う側はネットワーク通したり非同期になる部分がどこにもない と思うかもですが await があると同期的な関数でも Promise をかぶせて非同期処理にされます
await のところでいったん他の処理が入ります
fn1 関数は 受け取る cb 関数が async 関数の可能性もあるので まとめて await するという作りになってます
fn1 が async 関数なので普通にこれを呼び出してるなら非同期が入るとわかるかもしれませんが これがリスナのような返り値を使わない 登録しておいて呼び出されるような関数になっていると特に分かりづらいです
fn1 の返り値は使われず fn2 を実行した結果 B が呼び出されるような場合なら fn1 が async 関数であっても問題ないので cb の処理を分岐して
async function fn1(cb){
const value = isAsyncFunction(cb) ? await cb({option}) : cb({option})
fn2(value)
}
にすれば cb が非同期関数でないなら fn2 の呼び出しまで同期的に行えます
もし fn1 の返り値を使うのなら fn1 を async 関数にすると cb が同期関数であっても fn1 の返り値が Promise になるのでそこで非同期になってしまいます
この場合は fn1 を通常の関数にして cb が非同期関数のときには then/catch で Promise 制御する必要があります
function fn1(cb){
if(isAsyncFunction(cb)){
return cb({option}).then(value => {
fn2(value)
})
}else{
const value = cb({option})
fn2(value)
}
}
isAsyncFunction という関数は標準のものではないので こういう感じで用意する必要があります
const isAsyncFunction = fn => fn.constructor.name === "AsyncFunction"