BloomScheme Blog

株式会社ブルームスキーム公式ブログ

【JavasScript】PromiseManagerを作る

非同期処理をPromiseで書くとネストが鬱陶しいので基本的にasync/awaitでかきたいですが、書き始めるとはたと気づく問題があります。 記法はすっきりしたけど並列ダウンロードが処理できません。 複数画像をロードしてから処理したい、という場合、各画像ロードを逐次処理させるのは無駄に処理時間が長くなるので望ましくありません。

そこで調べてみるとPromise.all()という便利メソッドがあらかじめ用意されています。 使ってみます。

const a = fetch("https://pbs.twimg.com/media/D1B3i8yVYAAeXn4.png")
const b = fetch("https://pbs.twimg.com/media/D1B3ZmxVAAEpGzC.png")
const c = fetch("https://pbs.twimg.com/media/D1Bin9kU4AIAJyG.jpg")
const result = await Promise.all([a,b,c])
console.log("a,b,c", a,b,c)
console.log("result", result)

f:id:BloomScheme:20190316225629p:plain

a,b,cにはPromiseが入りっぱなし(当然ですが)。Promise.all()の返り値は配列として結果が格納されています。 単純に配列として受け取ればいいケースなら問題ないですが、用途の違うデータをそれぞれダウンロードさせて一気に処理したい、というときには困ります。最低限名前つきで管理したいです。

ということでPromiseManagerをざっくり作成してみました。

class PromiseManager {
  constructor(){
      this.entries = []
      this.data = {}
  }

  entry(name, promise){
    this.entries.push({name, promise})
  }
  
  async all(){
    const allResult =  await Promise.all(this.entries.map(item => item.promise))

    allResult.map((value, i) => {
      this.data[this.entries[i].name] = value
    })

    return allResult
  }

  get(name){
    return this.data[name]
  }
}

使用例:

    const promiseManager = new PromiseManager()
    promiseManager.entry("a", fetch("https://pbs.twimg.com/media/D1B3i8yVYAAeXn4.png"))
    promiseManager.entry("b", fetch("https://pbs.twimg.com/media/D1B3ZmxVAAEpGzC.png"))
    promiseManager.entry("c", fetch("https://pbs.twimg.com/media/D1Bin9kU4AIAJyG.jpg"))
    await promiseManager.all()
    console.log("promiseManager.data", promiseManager.data)

f:id:BloomScheme:20190316230158p:plain

いいかんじに動いてますね。 汎用性を重視してPromiseを受け取るようにしていますが、例えば画像用途のみに限定してであれば

class ImageManager extends PromiseManager{
  entry(name, url){
    this.entries.push({name, fetch(url)})
  }
}

このようなものを作ったりすると運用が便利そうです。