uhyohyo.net

JavaScript初級者から中級者になろう

十五章第五回 User Timing

前回の続きです。前回はPerformanceEntryオブジェクトの具体例としてPerformanceRequestTimingオブジェクトを紹介しました。今回はもう一つの例です。

User Timingというのは、時間計測のタイミングをプログラムから指示できるものです。つまり、JavaScriptの処理に時間がかかる場合、User Timingを利用して時間を計測することで、どこに時間がかかっているかなどを見極めることができます。JavaScriptでもDateオブジェクトを用いて現在時刻を取得し、それを用いて時間の計測をすることも可能ですが、User Timingではより高精度な計測が可能であるとされています。

前回のPerformanceRequestTimingのようにページから読み込むものは、JavaScriptプログラムからいじれる範疇ではないためそれ用のAPIが用意されていると考えられます。逆に、JavaScriptプログラムで扱える部分の時間計測はこのUser Timingにより可能なわけです。

PerformanceMark

User Timingにより行えることの一つは、マークを登録することです。マークというのは、ある瞬間の時刻に名前を付けて保存しておけるものです。それは、performanceが持つmarkメソッドにより行ないます。引数はマークの名前です。例えば、

performance.mark("mymark");

のようにすると、その瞬間の時刻が"mymark"という名前でperformanceオブジェクトに登録されます。

時刻が登録されるとはどういうことかというと、もちろんPerformanceEntryが登録されるのです。markメソッドにより登録されるPerformanceEntryオブジェクトのことをPerformanceMarkといいます。特に新しいプロパティはありませんが、PerformanceEntryが持つ各プロパティは次のようにセットされます。

name
markメソッドに渡された、マークの名前です。
entryType
"mark"になります。
startTime
マークが作成された瞬間の時刻です。
duration
0になります。

durationが0になるのは、今回は時間の範囲を表すのではなくある点での時刻を表すからですね。前回のPerformanceRequestTimingはやけに大量のプロパティがありましたが、今回は追加はなくこの4つだけですっきりしています。startTimeは前回同様、performance.timing.navigationStartを0とする時間になっています。

ここまでを試してみましょう。

performance.mark("mymark");
console.log(performance.getEntriesByName("mymark")[0]);

markメソッドによって追加されたPerformanceMarkオブジェクトはPerformanceEntryを継承したものなので、当然十五回三回で紹介したgetEntriesなどのメソッドで得ることができます。

なお、同じ名前で複数回markメソッドを呼び出した場合、別々のPerformanceMarkオブジェクトとして複数登録されます。

他にperformanceはclearMarksメソッドを持っています。これは引数にマークの名前を渡すことで、そのPerformanceMarkを除去します。つまり、上のサンプルの実行後にマークを除去するには、

performance.clearMarks("mymark");

とします。

なお、引数を省略して呼び出すこともできます。その場合は名前に関わらず全てのマークを除去します。

PerformanceMeasure

User Timingでは、ある瞬間の時刻を記録する他にももう一つできることができます。それは、2つのマークの間の時間を測ることです。時間を測るのは、今度はmarkメソッドではなくmeasureメソッドでできるのですが、実は計測した結果も一種のPerformanceEntryとしてPerformanceに登録されます。このオブジェクトをPerformanceMeasureといいます。

measureメソッドは3つの引数をとります。1つ目はmeasureの名前、2つ目は開始点のマークの名前、3つ目は終了点のマークの名前です。これにより、開始点の時刻と終了点の時刻の差がとられ、PerformanceMeasureオブジェクトとして登録されます。このオブジェクトのプロパティは以下のようにセットされます。

name
measureメソッドに渡された名前です。
entryType
"measure"になります。
startTime
開始点の時刻です。
duration
開始点の時刻と終了点の時刻の差です。

つまり、目的の時間はdurationプロパティから得られるということですね。

なお、開始点と終了点はマークの名前を指定しますが、同じ名前で複数のマークが登録されていた場合、最も新しいものが使用されます。

ここまでの内容をまとめたのがサンプル1です。一般に、innerHTML(二章第三回)を書き換えることでHTMLを生成するのは速度が遅いとされています。HTMLの解析・DOMの再構築が必要になるからです。そこで、innerHTMLへの代入を10000回行なってかかった時間を測定してみました。筆者の環境だと80ミリ秒前後でした。

ちなみに、innerHTMLを使うかわりにcreateElementなどを使って同じことを行なって時間を測るサンプル2もついでに用意してみました。innerHTMLを使うのに比べてソースが煩雑になった印象を受けますが、自分の場合は20〜30ミリ秒ほど動作が速くなりました。

これらのサンプルでは、markメソッドはmeasureメソッドでマークを利用するためだけに実行されており、markメソッドにより追加されたPerformanceMarkオブジェクトを直接は利用していませんね。

ちなみに、measureメソッドの第二引数・第三引数はマーク名ですが、他にもPerformanceTimingのプロパティ(十五章第二回で解説)名を指定することで、そのタイミングとの差をとることができます。たとえば、

performance.measure("test","navigationStart","end");

のようなことが可能です。

また、第三引数を省略した場合は、第二引数で指定した開始点と現在時刻の差が得られます。さらに、第二引数も省略した場合、navigationStartから現在時刻までの差となります。

そのため前述のサンプルでは、実はendマークを登録した直後にmeasureメソッドを実行しているので、endマークを登録してそれを利用するかわりにmeasureメソッドの第三引数の省略を利用することもできました。

最後に、clearMeasuresメソッドを紹介しておきます。これは先に紹介したclearMarksメソッドのmeasure版で、動作は同じです。引数が省略できる点も同じです。

補足

じつは、推奨マーク名というものが決まっています。ある特定のタイミングをマークにする場合は、この名前をおすすめするというものです。以下のマーク名が指定されています。

mark_fully_loaded
ページの読み込みが完全に完了したタイミング。
mark_fully_visible
ページがユーザーに利用可能になったタイミング。
mark_above_the_fold
ページの全内容が表示されたタイミング。
mark_time_to_user_action
ページでユーザーが初めて何か行動したタイミング。

いずれも、実際にページの動作の軽快さなどを評価するにあたってはよくつかわれる指標なのでしょう。

ただし、これは強制ではありませんし、推奨マーク名を使ったからといって何か嬉しい点があるわけではありません。

ただ、そう定められているので一応紹介だけしておきます。