简介
定义
高分辨率时间接口(High Resolution Time level 3) 提供了不受系统时钟偏差影响的起始时间记录(time origin)和亚毫秒级计时接口(DOMHighResTimeStamp)
【扩展】
- 原文亚毫秒(sub-millisecond)指精度高于毫秒,目前实现精度为微秒(1微妙等于百万分之一秒,10^(-6),1毫秒等于千分之一秒,10^(-3));
- Date 为毫秒近似计时
背景
ECMA-262 定义的 Date 对象记录自 1970 年 1 月 1 日 00:00:00 (UTC) 到当前时间的毫秒数,时间戳类型为DOMTimeStamp
存在问题
- 不是稳定的单调时钟,会根据目标系统的不同出现偏差(如不同worker之间的起始时间记录)
- 不能提供亚秒级的时间记录
解决方案:
基于以上问题,High Resolution Time提供精度更高(亚毫秒)、更稳定的performance接口,其时间戳类型为DOMHighResTimeStamp
performance接口暴露在worker 和 window全局中,提供了系列高性能计算接口,其中performance.timeOrigin记录了稳定的起始时间
定义对象
Time Origin
起始时间计算方式:
- 对于window 对象,
- 如果没有前document,timeOrigin表示从浏览器上下文建立时计时
- 如果有前document且unload事件完毕,timeOrigin表示用户确认前页面unload的时间点?
- 其他情况为加载window最新document时间
- 对于WorkerGlobalScope全局对象,timeOrigin是worker创建的时间
- 其他情况, time origin为undefined
DOMHighResTimeStamp:
double类型的时间计量类,精确至【5微妙】
数据结构
typedef double DOMHighResTimeStamp;
Performance
数据结构
interface Performance : EventTarget {
DOMHighResTimeStamp now();
readonly attribute DOMHighResTimeStamp timeOrigin;
[Default] object toJSON();
};
基本使用
Eamples:比较全局时间和worker中的相对时间
/** worker.js */
onconnect = function(e) {
const port = e.ports[0];
port.onmessage = async function(e) {
// worker 执行任务
const task_start = performance.now();
const result = await new Promise((resolve) => {
setTimeout(() => {
resolve(e.data)
}, 1000)
})
const task_end = performance.now();
// 发送epoch-relative时间戳至其他上下文
const date = Date.now()
const timeOrigin = performance.timeOrigin
port.postMessage({
'w_task': 'Some worker task',
'w_task_start': task_start,
'w_task_end': task_end,
'w_timeOrigin_add_start_time': task_start + timeOrigin,
'w_timeOrigin_end_time': task_end + timeOrigin,
'w_timeOrigin': timeOrigin,
'w_date': date,
'index': result,
});
}
}
/** index.js */
const both = document.querySelector('#both');
function reportEventToAnalytics(obj) {
console.log(obj)
}
// 转换 worker 时间戳为document 的 time origin 时间戳
if (!!window.SharedWorker) {
const worker = new SharedWorker('worker.js');
both.onclick = function() {
const performanceNow = performance.now()
const dateNow = Date.now()
const timeOrigin = performance.timeOrigin
console.log('SendMessage to worker: ', performanceNow, dateNow, timeOrigin)
worker.port.postMessage({origin: 'both', dateNow, performanceNow,timeOrigin})
}
worker.port.onmessage = function (event) {
console.log('Receive message from worker.js')
const msg = event.data;
reportEventToAnalytics(Object.assign({}, msg));
const timeOrigin = performance.timeOrigin
// 转换 epoch-relative 时间戳为docuemnt的 time origin
msg.index_start_time = msg.w_timeOrigin_add_start_time - timeOrigin;
msg.index_end_time = msg.w_timeOrigin_end_time - timeOrigin;
reportEventToAnalytics(msg);
}
worker.port.start()
} else {
console.log(`Your brower doesn't support SharedWorker API`)
}
在worker中,timeOrigin保持在一个固定值,所以可以用timeOrigin计算较准确的时间点
【扩展】经测试,(performance.timeOrigin+performance.now()) - Date.now() 两小时的误差约 8毫秒
参考
High Resolution Time Level 3: www.w3.org/TR/hr-time-…