クライアントで API の呼び出しにかかった時間を Resource Timing API を用いて計測する方法を解説します。
Resource Timing API とは
Resource Timing API とは Web ページで読み込まれるリソースの読み込みに要した時間を詳細に取得するための API で、 XMLHttpRequest
に限らず <img>
などの取得に要する時間を知ることもできます。
Resource Timing API では、その結果を PerformanceResourceTiming
というオブジェクトに格納します。PerformanceResourceTiming
は Resource Timing Level 1 で定められており、 2017 年 3 月に Candidate Recommendation となったので、もう使ってしまって問題ないでしょう。
PerformanceResourceTiming
を取得する
ある URL に対応する PerformanceResourceTiming
を Performance.getEntriesByName
を使って取得するコードを以下に示します:
/**
* @param {String} url
* @return {?PerformanceResourceTiming}
*/
function getResourceTiming(url) {
// Performance API が使えない場合は null を返す
if (!window.performance) return null
// getEntriesByName が無かったら null を返す。 Safari 9, 10
if (!performance.getEntriesByName) return null
// 同一 URL に繰り返しリクエストしている場合 resources は複数になる。
const resources = performance.getEntriesByName(url, "resource")
// 最後のやつを return する
return resources[resources.length - 1]
}
たとえばある XMLHttpRequest
に対応する PerformanceResourceTiming
を求めるには:
getResourceTiming(xhr.responseURL)
各種時間を取得する
取得した PerformanceResourceTiming
オブジェクトからリクエストにかかった各種時間を求めます。なおここから先のサンプルコードは次のコード中の /* ここ */
の中にいるものとして記述します。
const resource = getResourceTiming(url)
if (resource) { /* ここ */ }
DNS lookup にかかった時間
resource.domainLookupEnd - resource.domainLookupStart
TCP コネクションにかかった時間
resource.connectEnd - resource.connectStart
SSL ハンドシェイクにかかった時間
resource.connectEnd - resource.secureConnectionStart
リダイレクトにかかった時間
resource.redirectEnd - resource.redirectStart
TTFB (Time To First Byte)
resource.responseStart - resource.startTime
全体の時間
XMLHttpRequest
を send してからリダイレクトなども含めて全てのデータを受信し終えるまでにかかった時間は
resource.duration
です。これは resource.responseEnd - resource.startTime
と同じです。
クロスオリジン
セキュリティのため Resource Timing API も同一生成元ポリシーに従います。オリジンをまたいで Resource Timing API を使いたい場合 Timing-Allow-Origin
ヘッダをつける必要があります。
もし同一生成元ポリシーに引っかかっている場合、生成される PerformanceResourceTiming
の以下のプロパティが全て 0
を返すようになります:
redirectStart
redirectEnd
domainLookupStart
domainLookupEnd
connectStart
connectEnd
secureConnectionStart
requestStart
responseStart
そのため、次のようなコードで同一生成元ポリシーに違反したものかどうかを知ることができます:
// 同一生成元ポリシーに違反している場合 false になる
!!response.requestStart
なお同一生成元ポリシーに違反している場合でも duration
startTime
responseEnd
は使うことができます。