hookehuyr

refactor(离线预约缓存): 重构轮询逻辑并增加网络状态监听

- 将分散的状态变量整合为 polling_state 对象
- 增加网络状态监听功能,仅在网络可用时运行轮询
- 新增 enable/disable 方法管理轮询生命周期
- 优化选项处理和状态管理逻辑
......@@ -13,7 +13,7 @@ import { saveCurrentPagePath, hasAuth, silentAuth, navigateToAuth } from '@/util
import Taro from '@tarojs/taro'
import { refresh_offline_booking_cache, has_offline_booking_cache } from '@/composables/useOfflineBookingCache'
import { is_usable_network, get_network_type } from '@/utils/network'
import { start_offline_booking_cache_polling } from '@/composables/useOfflineBookingCachePolling'
import { enable_offline_booking_cache_polling } from '@/composables/useOfflineBookingCachePolling'
import { weak_network_text, get_weak_network_modal_use_cache_options } from '@/utils/uiText'
let has_shown_network_modal = false
......@@ -128,7 +128,7 @@ const App = createApp({
// 有授权时预加载数据
try_preload_when_online()
// 启动离线预约缓存轮询
start_offline_booking_cache_polling()
enable_offline_booking_cache_polling()
return
}
......@@ -140,7 +140,7 @@ const App = createApp({
// 授权成功后预加载数据
try_preload_when_online()
// 启动离线预约缓存轮询
start_offline_booking_cache_polling()
enable_offline_booking_cache_polling()
} catch (error) {
console.error('静默授权失败:', error)
// 授权失败则跳转至授权页面
......
import { ref, onMounted, onUnmounted } from 'vue'
import Taro from '@tarojs/taro'
import { refresh_offline_booking_cache } from '@/composables/useOfflineBookingCache'
import { get_network_type, is_usable_network } from '@/utils/network'
let polling_timer_id = null
let polling_running = false
let polling_in_flight = false
let polling_ref_count = 0
/**
* @description: 轮询状态
* @property {Number} timer_id 轮询定时器id
* @property {Boolean} running 是否正在轮询
* @property {Boolean} in_flight 是否正在刷新
* @property {Number} ref_count 引用计数
* @property {Boolean} app_enabled 是否启用应用
* @property {Object} last_options 最后一次选项
* @property {Boolean} network_usable 网络可用性
* @property {Boolean} has_network_listener 是否已注册网络监听器
* @property {Function} network_listener 网络监听器
* @property {Promise} network_listener_promise 网络监听器Promise
*/
const polling_state = {
timer_id: null, // 轮询定时器id
running: false, // 是否正在轮询
in_flight: false, // 是否正在刷新
ref_count: 0, // 引用计数
app_enabled: false, // 是否启用应用
last_options: null, // 最后一次选项
network_usable: null, // 网络可用性
has_network_listener: false, // 是否已注册网络监听器
network_listener: null, // 网络监听器
network_listener_promise: null, // 网络监听器Promise
}
/**
* @description: 归一化选项
* @param {Object} options 选项
* @return {Object} 归一化后的选项
*/
const normalize_options = (options) => {
polling_state.last_options = options || polling_state.last_options || {}
return polling_state.last_options
}
/**
* @description: 刷新离线预约缓存一次
......@@ -13,16 +48,115 @@ let polling_ref_count = 0
*/
const run_refresh_once = async (options) => {
if (polling_in_flight) return
polling_in_flight = true
if (polling_state.in_flight) return
polling_state.in_flight = true
try {
await refresh_offline_booking_cache({ force: !!options?.force })
} finally {
polling_in_flight = false
polling_state.in_flight = false
}
}
/**
* @description: 更新网络可用性
*/
const update_network_usable = async () => {
const type = await get_network_type()
polling_state.network_usable = is_usable_network(type)
}
/**
* @description: 判断是否需要运行轮询
* @return {Boolean} 是否需要运行轮询
*/
const should_run_polling = () => {
if (polling_state.ref_count <= 0) return false
if (polling_state.network_usable === false) return false
if (polling_state.network_usable === null) return false
return true
}
/**
* @description: 确保轮询已启动
* @param {Object} options 选项
*/
const ensure_polling_started = (options) => {
start_offline_booking_cache_polling(options)
}
/**
* @description: 确保网络监听器已注册
*/
const ensure_network_listener = async () => {
if (polling_state.has_network_listener) {
await update_network_usable()
return
}
if (polling_state.network_listener_promise) {
await polling_state.network_listener_promise
return
}
polling_state.network_listener_promise = (async () => {
polling_state.has_network_listener = true
await update_network_usable()
polling_state.network_listener = (res) => {
const is_connected = res?.isConnected !== false
const type = res?.networkType || 'unknown'
polling_state.network_usable = is_connected && is_usable_network(type)
if (!polling_state.network_usable) {
stop_offline_booking_cache_polling()
return
}
if (should_run_polling()) {
ensure_polling_started(polling_state.last_options || {})
}
}
try {
Taro.onNetworkStatusChange(polling_state.network_listener)
} catch (e) {
polling_state.has_network_listener = false
polling_state.network_listener = null
polling_state.network_usable = null
console.error('注册网络监听失败:', e)
}
})()
try {
await polling_state.network_listener_promise
} finally {
polling_state.network_listener_promise = null
}
}
/**
* @description: 注销网络监听器
*/
const teardown_network_listener = () => {
if (!polling_state.has_network_listener) return
if (polling_state.ref_count > 0) return
polling_state.has_network_listener = false
if (polling_state.network_listener && typeof Taro.offNetworkStatusChange === 'function') {
try {
Taro.offNetworkStatusChange(polling_state.network_listener)
} catch (e) {
polling_state.network_listener = null
}
}
polling_state.network_listener = null
polling_state.network_usable = null
}
/**
* @description: 启动离线预约缓存轮询
* @param {Object} options 选项
* @param {Number} options.interval_ms 轮询间隔,单位毫秒
......@@ -30,16 +164,18 @@ const run_refresh_once = async (options) => {
*/
export const start_offline_booking_cache_polling = (options) => {
normalize_options(options)
if (!should_run_polling()) return
const interval_ms = Number(options?.interval_ms || 60000)
if (polling_running) return
if (polling_state.running) return
polling_running = true
polling_state.running = true
if (options?.immediate !== false) {
run_refresh_once(options)
}
polling_timer_id = setInterval(() => {
polling_state.timer_id = setInterval(() => {
run_refresh_once(options)
}, interval_ms)
}
......@@ -49,13 +185,32 @@ export const start_offline_booking_cache_polling = (options) => {
*/
export const stop_offline_booking_cache_polling = () => {
if (!polling_timer_id) {
polling_running = false
if (polling_state.timer_id) {
clearInterval(polling_state.timer_id)
polling_state.timer_id = null
}
polling_state.running = false
}
export const enable_offline_booking_cache_polling = (options) => {
normalize_options(options)
if (polling_state.app_enabled) {
ensure_network_listener().then(() => ensure_polling_started(polling_state.last_options || {}))
return
}
clearInterval(polling_timer_id)
polling_timer_id = null
polling_running = false
polling_state.app_enabled = true
polling_state.ref_count += 1
ensure_network_listener().then(() => ensure_polling_started(polling_state.last_options || {}))
}
export const disable_offline_booking_cache_polling = () => {
if (!polling_state.app_enabled) return
polling_state.app_enabled = false
polling_state.ref_count = Math.max(0, polling_state.ref_count - 1)
if (polling_state.ref_count === 0) {
stop_offline_booking_cache_polling()
teardown_network_listener()
}
}
/**
......@@ -73,18 +228,19 @@ export const use_offline_booking_cache_polling = (options) => {
const start = () => {
if (!enabled) return
polling_ref_count += 1
// TAG: 每次启动轮询时,都增加引用计数
start_offline_booking_cache_polling(options)
polling_state.ref_count += 1
ensure_network_listener().then(() => {
ensure_polling_started(options)
})
is_running.value = true
}
const stop = () => {
if (!is_running.value) return
polling_ref_count = Math.max(0, polling_ref_count - 1)
// TAG: 每次停止轮询时,都减少引用计数
if (polling_ref_count === 0) {
polling_state.ref_count = Math.max(0, polling_state.ref_count - 1)
if (polling_state.ref_count === 0) {
stop_offline_booking_cache_polling()
teardown_network_listener()
}
is_running.value = false
}
......