#何がしたいか
エクセルライクなスプレッドをWebアプリに組み込めるライブラリSpreadjsがあります。
その初期化処理中はローディングを出して、初期化が終わったらスプレットを出す実装の中で、
非同期の概念が少し理解できたので、まとめてみました。
#理解できないがとりあえず実現できたコード
import GC from '@grapecity/spread-sheets'
import { Column, SpreadSheets, Worksheet } from '@grapecity/spread-sheets-react'
import '@grapecity/spread-sheets-resources-ja'
import { useState } from "react"
export const TestSheet = () => {
const [isLoading, setIsLoading] = useState(true)
const initSpread = (spread: GC.Spread.Sheets.Workbook) => {
return new Promise<void>(resolve => {
setTimeout(() => {
// 3秒の重い処理を想定
resolve()
}, 3000)
})
}
return (
<>
{isLoading ? <h1>Loading....</h1> : <></>}
<div style={{ visibility: isLoading ? 'hidden' : 'visible' }}>
<SpreadSheets workbookInitialized={spread => {
initSpread(spread).then(() => setIsLoading(false))
}}>
<Worksheet >
<Column />
</Worksheet>
</SpreadSheets>
</div>
</>
)
}
#非同期難しい
残念ながら、私はこのコードがなぜ上手くいくのか、理解できませんでした。
Promiseじゃなくて、async/awaitを使用したいし、setTimeoutも不要だと思ってました。
そして、色々消したり、付け加えたりしたのですが、うまくいきませんでした。
そこで、書籍を読み漁りました。
#読み漁りの結果
プログラミング TypeScript スケールするJavaScript アプリケーション開発
上記書籍より、少しイメージがつかめたので説明します。
##ローディングのstateを変更するタイミング
重い処理が終わったタイミングでstateを変更すればよいので、
シンプルに重い処理の後に記述すればよく、.then()とか、awaitは不要です。
const initSpread = (spread: GC.Spread.Sheets.Workbook) => {
return new Promise<void>(resolve => {
setTimeout(() => {
// 3秒の重い処理を想定
setIsLoading(false) //new !!
resolve()
}, 3000)
})
}
//省略
return (
<SpreadSheets workbookInitialized={spread => {
initSpread(spread) //.then(() => setIsLoading(false))
}}>
)
##Promiseやasyncが非同期を生み出すという勘違い
Promiseや、そのシンタックスシュガーであるasyncを付けるだけで非同期にはなりません。
非同期は、JavaScriptプラットフォームによって提供されているネイティブな非同期APIを呼び出す必要があります。
今回は、setTimeout()で非同期APIを呼び出しています。
そして、非同期APIを呼び出せば、その時点で、呼び出している関数(initSpread)も非同期になります。
よって、Promiseやasyncは不要です。
const initSpread = (spread: GC.Spread.Sheets.Workbook) => {
setTimeout(() => {
// 3秒の重い処理を想定
setIsLoading(false)
}, 3000)
}
##待つ必要がなければ、await不要→asyncも不要
initSpreadはバックグラウンドで勝手に処理されて、最後にloadingのstateをオフにすればよいので、
待つ必要はありません。asyncはawaitを書く際に必要になるものなので、
待つ必要がなければ、何も記述する必要はありません。
非同期処理がただ駆け抜けるだけです。
#まとめ
ただでさえ、非同期がわかっていないのに、Reactのレンダリングするタイミングも相まって、
非同期の理解がなかなか進みませんでした。
レンダリング内でawaitすれば、レンダリングが止まるのも、当たり前だと理解できるようになりました。