🚆

Promise.allによるストレスフリーUI実現

2 min read

仰々しく書きましたが、要するに「APIデータ取得反映のレイテンシーを無くそう」ということです。

実際にどういったことなのか、検証してみましょう。

jsonデータを作成するために、以下のサイトを利用して作成しました。
JSON GENERATOR

用意する環境

今回はコンポーネントの再利用と自身の馴染みのあるReactを使用して検証します。
APIデータはlight.jsonfat.jsonを使用します(ネーミングセンス皆無)。
だいたいlightは10, fatは100000データを用意してます。極端ですね。

さて、この異なるサイズのデータを取得してリストに反映してみましょう。

律儀にいく

とりあえず、fetchを利用してデータを取ってきてみましょうか。

App.jsx
import React, { useEffect, useState } from 'react'
import './App.css'

const App = () => {
  const [lightData, setLightData] = useState([])
  const [fatData, setFatData] = useState([])

  useEffect(() => {
    fetch('/light.json')
      .then(res => res.json())
      .then(data => {
        setLightData(data)
      })
    fetch('/fat.json')
      .then(res => res.json())
      .then(data => {
        setFatData(data)
      })
  }, [])

  return (
    <div className='container'>
      <h1>PromiseAll Test</h1>
      <section>
        <h2>LightData</h2>
        { lightData.length ? <List data={lightData}/> : <Loading />}
      </section>
      <section>
        <h2>FatData</h2>
        { fatData.length ? <List data={fatData}/> : <Loading /> }
      </section>
    </div>
  )
}

const List = ({data}) => {
  return (
    <ul className='list'>
      { data?.map(({ name, _id }) => <li key={_id}>{ name }</li>) }
    </ul>
  )
}

const Loading = () => (<p style={{ textAlign: 'center' }}><img src="/loading.gif" alt=""/></p>)

export default App

結果をみてみましょう。yarn start

反映はされていますが、どうやら取得のデータ量が違うので反映までに差が生まれてしまっています。
リクエストに対しての応答、つまりレイテンシが大きくなってしまっているのが原因です。

ではどうすれば同時に綺麗に反映できるでしょうか。

Promise.all使ってみよう

ここで本題に入ります。

Promise.all(iterable) メソッドは単一の Promise を返し、これは引数 iterable の中のすべての Promise が解決されるか、引数 iterable の中に Promise がない場合に解決されます。

MDN web docsより Promise.all()

App.jsx
import React, { useEffect, useState } from 'react'
import './App.css'

const App = () => {
  ...

  useEffect(() => {
    const resL = fetch('/light.json').then(res => res.json()).then(data => data)
    const resF = fetch('/fat.json').then(res => res.json()).then(data => data)
    Promise.all([resL, resF])
      .then(values => {
        setLightData(values[0])
        setFatData(values[1])
      })
  }, [])

  ...
}


同じタイミングでデータを表示させることができました。👏

感想

複数データを扱う際は積極的に使用してもよいかなと感じました。

取得からの反映されるまで、感じる違和感があれば方法を考えて解決していくようにしていきたいです。

JavaScriptにはデータを使用するために様々なメソッドがありますので、検討してみるのも良いかな、と感じました。

Discussion

ログインするとコメントできます