Files
draw/src/services/networkService.ts
2025-09-18 23:34:55 +08:00

159 lines
4.1 KiB
TypeScript

export interface NetworkStatus {
isOnline: boolean;
lastOnlineTime?: Date;
lastOfflineTime?: Date;
lastCheck: Date;
}
export type NetworkStatusCallback = (status: NetworkStatus) => void;
export class NetworkService {
private static instance: NetworkService;
private status: NetworkStatus;
private callbacks: Set<NetworkStatusCallback> = new Set();
private checkInterval?: NodeJS.Timeout;
private constructor() {
const now = new Date();
this.status = {
isOnline: navigator.onLine,
lastOnlineTime: navigator.onLine ? now : undefined,
lastOfflineTime: !navigator.onLine ? now : undefined,
lastCheck: now
};
this.initializeListeners();
this.startPeriodicCheck();
}
public static getInstance(): NetworkService {
if (!NetworkService.instance) {
NetworkService.instance = new NetworkService();
}
return NetworkService.instance;
}
private initializeListeners(): void {
window.addEventListener('online', this.handleOnline.bind(this));
window.addEventListener('offline', this.handleOffline.bind(this));
}
private handleOnline(): void {
console.log('网络连接已恢复');
this.updateStatus(true);
}
private handleOffline(): void {
console.log('网络连接已断开');
this.updateStatus(false);
}
private updateStatus(isOnline: boolean): void {
const now = new Date();
this.status = {
...this.status,
isOnline,
lastOnlineTime: isOnline ? now : this.status.lastOnlineTime,
lastOfflineTime: !isOnline ? now : this.status.lastOfflineTime,
lastCheck: now
};
// 通知所有监听器
this.callbacks.forEach(callback => {
try {
callback(this.status);
} catch (error) {
console.error('网络状态回调执行错误:', error);
}
});
}
private startPeriodicCheck(): void {
// 每30秒检查一次网络连接
this.checkInterval = setInterval(() => {
this.checkNetworkConnectivity();
}, 30000);
}
private async checkNetworkConnectivity(): Promise<void> {
try {
// 使用浏览器的在线状态作为主要判断依据
const browserOnline = navigator.onLine;
if (!browserOnline) {
if (this.status.isOnline) {
console.log('浏览器检测到网络断开');
this.updateStatus(false);
}
return;
}
// 如果浏览器认为在线,直接信任浏览器的判断
// 避免额外的网络请求导致不必要的错误日志
if (browserOnline !== this.status.isOnline) {
this.updateStatus(browserOnline);
} else {
// 即使状态没变,也要更新检查时间
this.status.lastCheck = new Date();
}
} catch (error) {
// 检测过程出错,保持当前状态
console.warn('网络状态检测出错:', error);
}
}
public getStatus(): NetworkStatus {
return { ...this.status };
}
public isOnline(): boolean {
return this.status.isOnline;
}
public subscribe(callback: NetworkStatusCallback): () => void {
this.callbacks.add(callback);
// 立即调用一次回调,提供当前状态
callback(this.status);
// 返回取消订阅函数
return () => {
this.callbacks.delete(callback);
};
}
public async waitForOnline(timeout: number = 30000): Promise<boolean> {
if (this.status.isOnline) {
return true;
}
return new Promise((resolve) => {
const timeoutId = setTimeout(() => {
unsubscribe();
resolve(false);
}, timeout);
const unsubscribe = this.subscribe((status) => {
if (status.isOnline) {
clearTimeout(timeoutId);
unsubscribe();
resolve(true);
}
});
});
}
public destroy(): void {
window.removeEventListener('online', this.handleOnline.bind(this));
window.removeEventListener('offline', this.handleOffline.bind(this));
if (this.checkInterval) {
clearInterval(this.checkInterval);
}
this.callbacks.clear();
}
}
export const networkService = NetworkService.getInstance();
export default NetworkService;