85 lines
2.4 KiB
JavaScript
85 lines
2.4 KiB
JavaScript
// 简单的 localStorage 心跳锁实现(兼容性回退)
|
|
// 若浏览器支持 BroadcastChannel,可替换为更即时的通讯方式
|
|
|
|
(function () {
|
|
const LOCK_KEY = 'calendar_instance_lock';
|
|
const HEARTBEAT_INTERVAL = 2000; // ms
|
|
const STALE_THRESHOLD = 5000; // ms
|
|
|
|
const instanceId = Date.now() + '-' + Math.random().toString(36).slice(2);
|
|
|
|
function now() { return Date.now(); }
|
|
|
|
function tryAcquire() {
|
|
const raw = localStorage.getItem(LOCK_KEY);
|
|
if (raw) {
|
|
try {
|
|
const obj = JSON.parse(raw);
|
|
if (now() - obj.heartbeat < STALE_THRESHOLD) {
|
|
// 有活跃实例
|
|
return false;
|
|
}
|
|
} catch (e) {
|
|
// malformed, ignore
|
|
}
|
|
}
|
|
// 申请锁
|
|
localStorage.setItem(LOCK_KEY, JSON.stringify({ id: instanceId, heartbeat: now() }));
|
|
return true;
|
|
}
|
|
|
|
function heartbeat() {
|
|
const raw = localStorage.getItem(LOCK_KEY);
|
|
if (!raw) return;
|
|
try {
|
|
const obj = JSON.parse(raw);
|
|
if (obj.id === instanceId) {
|
|
obj.heartbeat = now();
|
|
localStorage.setItem(LOCK_KEY, JSON.stringify(obj));
|
|
} else {
|
|
// 被其他实例抢占,通知并退出
|
|
alert('你好,每次只能运行一个程序。你已经运行了。');
|
|
cleanupAndExit();
|
|
}
|
|
} catch (e) { /* ignore */ }
|
|
}
|
|
|
|
function cleanupAndExit() {
|
|
try { // 尝试清理自己持有的锁
|
|
const raw = localStorage.getItem(LOCK_KEY);
|
|
if (raw) {
|
|
const obj = JSON.parse(raw);
|
|
if (obj.id === instanceId) {
|
|
localStorage.removeItem(LOCK_KEY);
|
|
}
|
|
}
|
|
} catch (e) {}
|
|
// 停止应用初始化(这里简单重载或关闭页面)
|
|
window.close && window.close();
|
|
}
|
|
|
|
if (!tryAcquire()) {
|
|
alert('你好,每次只能运行一个程序。你已经运行了。');
|
|
// 直接阻止后续初始化
|
|
throw new Error('Single instance active');
|
|
}
|
|
|
|
// 定时心跳
|
|
const hbTimer = setInterval(heartbeat, HEARTBEAT_INTERVAL);
|
|
|
|
// 在页面卸载时释放锁
|
|
window.addEventListener('beforeunload', function () {
|
|
try {
|
|
const raw = localStorage.getItem(LOCK_KEY);
|
|
if (raw) {
|
|
const obj = JSON.parse(raw);
|
|
if (obj.id === instanceId) {
|
|
localStorage.removeItem(LOCK_KEY);
|
|
}
|
|
}
|
|
} catch (e) {}
|
|
clearInterval(hbTimer);
|
|
});
|
|
|
|
// ... 应用其他初始化继续 ...
|
|
})(); |