159 lines
4.1 KiB
TypeScript
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; |