Promise.all について使い方のメモ

tl;dr;

複数の Promise を使用するときは Promise.all で解決すると待たなくて済む


事の発端として,複数の http リクエストを送る場合に,前のリクエストが完了するまで処理が進まなかった。 どうやったら前処理を待たずに擬似マルチで処理ができるのかなと思って手を動かして納得できたのでメモ。

検証準備

Promise を複数使用して試したい場合は単純に

new Promise(setTimeout(() => {}), 1000)

とかで十分検証できるとは思うけど,ちょっと味気ないので (勉強も兼ねて) 適当な express サーバーを立てて検証

サーバー側

ルートに対して timeout のクエリを指定すると,指定秒数待機してからレスポンスを返す簡単なサーバーを立てます。

import express from 'express'

const app = express()
const port = 3000

app.get('/', (req, res) => {
  const log = (type: string, time: number) => {
    console.log(`${Date()}: ${type} - ${time}`)
  }

  const timeout: number = parseInt(req.query.timeout as string)
  log('Request', timeout)

  setTimeout(() => {
    res.send(`waited ${timeout}`)
    log('Response', timeout)
  }, timeout * 1000 || 0)
})

app.listen(port, () => {
  console.log('start server')
})

クライアント側

import axios from 'axios'
import assert from 'assert'

axios.defaults.baseURL = 'http://localhost:3000'

const request = async (time: number) => {
  console.log(`timeout: ${time}`)
  assert.strictEqual(
    (await axios.get('/', { params: { timeout: time } })).data,
    `waited ${time}`
  )
}

axios で GET リクエストを送って,念の為 assert でチェックします。


単純に await を並べて書くと以下のような結果になります。

// Case A
const caseA = async () => {
  console.time('request')
  console.log('start')

  await request(3)
  await request(5)

  console.log('done')
  console.timeEnd('request')
}
start
timeout: 3
timeout: 5
done
request: 8.025s

これでは前のリクエストが完了するまで次の処理に進めないため時間がかかってしまいます。

次は Promise.all で解決する場合。

// Case B
const caseB = async () => {
  console.time('request')
  console.log('start')

  await Promise.all([request(3), request(5)])

  console.log('done')
  console.timeEnd('request')
}
start
timeout: 3
timeout: 5
done
request: 5.016s

以上のように待たずに同時に処理を開始することができました。

今回の検証では全てのリクエストに成功していましたが,一部で失敗してしまうと,reject を返してしまうそうです (まだ追えてないので後で調べる)。

Promise.all()#Promise.allのフェイルファストの挙動