refactor(离线预约缓存): 重构轮询逻辑并增加网络状态监听
- 将分散的状态变量整合为 polling_state 对象 - 增加网络状态监听功能,仅在网络可用时运行轮询 - 新增 enable/disable 方法管理轮询生命周期 - 优化选项处理和状态管理逻辑
Showing
2 changed files
with
180 additions
and
24 deletions
| ... | @@ -13,7 +13,7 @@ import { saveCurrentPagePath, hasAuth, silentAuth, navigateToAuth } from '@/util | ... | @@ -13,7 +13,7 @@ import { saveCurrentPagePath, hasAuth, silentAuth, navigateToAuth } from '@/util |
| 13 | import Taro from '@tarojs/taro' | 13 | import Taro from '@tarojs/taro' |
| 14 | import { refresh_offline_booking_cache, has_offline_booking_cache } from '@/composables/useOfflineBookingCache' | 14 | import { refresh_offline_booking_cache, has_offline_booking_cache } from '@/composables/useOfflineBookingCache' |
| 15 | import { is_usable_network, get_network_type } from '@/utils/network' | 15 | import { is_usable_network, get_network_type } from '@/utils/network' |
| 16 | -import { start_offline_booking_cache_polling } from '@/composables/useOfflineBookingCachePolling' | 16 | +import { enable_offline_booking_cache_polling } from '@/composables/useOfflineBookingCachePolling' |
| 17 | import { weak_network_text, get_weak_network_modal_use_cache_options } from '@/utils/uiText' | 17 | import { weak_network_text, get_weak_network_modal_use_cache_options } from '@/utils/uiText' |
| 18 | 18 | ||
| 19 | let has_shown_network_modal = false | 19 | let has_shown_network_modal = false |
| ... | @@ -128,7 +128,7 @@ const App = createApp({ | ... | @@ -128,7 +128,7 @@ const App = createApp({ |
| 128 | // 有授权时预加载数据 | 128 | // 有授权时预加载数据 |
| 129 | try_preload_when_online() | 129 | try_preload_when_online() |
| 130 | // 启动离线预约缓存轮询 | 130 | // 启动离线预约缓存轮询 |
| 131 | - start_offline_booking_cache_polling() | 131 | + enable_offline_booking_cache_polling() |
| 132 | return | 132 | return |
| 133 | } | 133 | } |
| 134 | 134 | ||
| ... | @@ -140,7 +140,7 @@ const App = createApp({ | ... | @@ -140,7 +140,7 @@ const App = createApp({ |
| 140 | // 授权成功后预加载数据 | 140 | // 授权成功后预加载数据 |
| 141 | try_preload_when_online() | 141 | try_preload_when_online() |
| 142 | // 启动离线预约缓存轮询 | 142 | // 启动离线预约缓存轮询 |
| 143 | - start_offline_booking_cache_polling() | 143 | + enable_offline_booking_cache_polling() |
| 144 | } catch (error) { | 144 | } catch (error) { |
| 145 | console.error('静默授权失败:', error) | 145 | console.error('静默授权失败:', error) |
| 146 | // 授权失败则跳转至授权页面 | 146 | // 授权失败则跳转至授权页面 | ... | ... |
| 1 | import { ref, onMounted, onUnmounted } from 'vue' | 1 | import { ref, onMounted, onUnmounted } from 'vue' |
| 2 | +import Taro from '@tarojs/taro' | ||
| 2 | import { refresh_offline_booking_cache } from '@/composables/useOfflineBookingCache' | 3 | import { refresh_offline_booking_cache } from '@/composables/useOfflineBookingCache' |
| 4 | +import { get_network_type, is_usable_network } from '@/utils/network' | ||
| 3 | 5 | ||
| 4 | -let polling_timer_id = null | 6 | +/** |
| 5 | -let polling_running = false | 7 | + * @description: 轮询状态 |
| 6 | -let polling_in_flight = false | 8 | + * @property {Number} timer_id 轮询定时器id |
| 7 | -let polling_ref_count = 0 | 9 | + * @property {Boolean} running 是否正在轮询 |
| 10 | + * @property {Boolean} in_flight 是否正在刷新 | ||
| 11 | + * @property {Number} ref_count 引用计数 | ||
| 12 | + * @property {Boolean} app_enabled 是否启用应用 | ||
| 13 | + * @property {Object} last_options 最后一次选项 | ||
| 14 | + * @property {Boolean} network_usable 网络可用性 | ||
| 15 | + * @property {Boolean} has_network_listener 是否已注册网络监听器 | ||
| 16 | + * @property {Function} network_listener 网络监听器 | ||
| 17 | + * @property {Promise} network_listener_promise 网络监听器Promise | ||
| 18 | + */ | ||
| 19 | + | ||
| 20 | +const polling_state = { | ||
| 21 | + timer_id: null, // 轮询定时器id | ||
| 22 | + running: false, // 是否正在轮询 | ||
| 23 | + in_flight: false, // 是否正在刷新 | ||
| 24 | + ref_count: 0, // 引用计数 | ||
| 25 | + app_enabled: false, // 是否启用应用 | ||
| 26 | + last_options: null, // 最后一次选项 | ||
| 27 | + network_usable: null, // 网络可用性 | ||
| 28 | + has_network_listener: false, // 是否已注册网络监听器 | ||
| 29 | + network_listener: null, // 网络监听器 | ||
| 30 | + network_listener_promise: null, // 网络监听器Promise | ||
| 31 | +} | ||
| 32 | + | ||
| 33 | +/** | ||
| 34 | + * @description: 归一化选项 | ||
| 35 | + * @param {Object} options 选项 | ||
| 36 | + * @return {Object} 归一化后的选项 | ||
| 37 | + */ | ||
| 38 | + | ||
| 39 | +const normalize_options = (options) => { | ||
| 40 | + polling_state.last_options = options || polling_state.last_options || {} | ||
| 41 | + return polling_state.last_options | ||
| 42 | +} | ||
| 8 | 43 | ||
| 9 | /** | 44 | /** |
| 10 | * @description: 刷新离线预约缓存一次 | 45 | * @description: 刷新离线预约缓存一次 |
| ... | @@ -13,16 +48,115 @@ let polling_ref_count = 0 | ... | @@ -13,16 +48,115 @@ let polling_ref_count = 0 |
| 13 | */ | 48 | */ |
| 14 | 49 | ||
| 15 | const run_refresh_once = async (options) => { | 50 | const run_refresh_once = async (options) => { |
| 16 | - if (polling_in_flight) return | 51 | + if (polling_state.in_flight) return |
| 17 | - polling_in_flight = true | 52 | + polling_state.in_flight = true |
| 18 | try { | 53 | try { |
| 19 | await refresh_offline_booking_cache({ force: !!options?.force }) | 54 | await refresh_offline_booking_cache({ force: !!options?.force }) |
| 20 | } finally { | 55 | } finally { |
| 21 | - polling_in_flight = false | 56 | + polling_state.in_flight = false |
| 22 | } | 57 | } |
| 23 | } | 58 | } |
| 24 | 59 | ||
| 25 | /** | 60 | /** |
| 61 | + * @description: 更新网络可用性 | ||
| 62 | + */ | ||
| 63 | + | ||
| 64 | +const update_network_usable = async () => { | ||
| 65 | + const type = await get_network_type() | ||
| 66 | + polling_state.network_usable = is_usable_network(type) | ||
| 67 | +} | ||
| 68 | + | ||
| 69 | +/** | ||
| 70 | + * @description: 判断是否需要运行轮询 | ||
| 71 | + * @return {Boolean} 是否需要运行轮询 | ||
| 72 | + */ | ||
| 73 | + | ||
| 74 | +const should_run_polling = () => { | ||
| 75 | + if (polling_state.ref_count <= 0) return false | ||
| 76 | + if (polling_state.network_usable === false) return false | ||
| 77 | + if (polling_state.network_usable === null) return false | ||
| 78 | + return true | ||
| 79 | +} | ||
| 80 | + | ||
| 81 | +/** | ||
| 82 | + * @description: 确保轮询已启动 | ||
| 83 | + * @param {Object} options 选项 | ||
| 84 | + */ | ||
| 85 | + | ||
| 86 | +const ensure_polling_started = (options) => { | ||
| 87 | + start_offline_booking_cache_polling(options) | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +/** | ||
| 91 | + * @description: 确保网络监听器已注册 | ||
| 92 | + */ | ||
| 93 | + | ||
| 94 | +const ensure_network_listener = async () => { | ||
| 95 | + if (polling_state.has_network_listener) { | ||
| 96 | + await update_network_usable() | ||
| 97 | + return | ||
| 98 | + } | ||
| 99 | + if (polling_state.network_listener_promise) { | ||
| 100 | + await polling_state.network_listener_promise | ||
| 101 | + return | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + polling_state.network_listener_promise = (async () => { | ||
| 105 | + polling_state.has_network_listener = true | ||
| 106 | + await update_network_usable() | ||
| 107 | + | ||
| 108 | + polling_state.network_listener = (res) => { | ||
| 109 | + const is_connected = res?.isConnected !== false | ||
| 110 | + const type = res?.networkType || 'unknown' | ||
| 111 | + polling_state.network_usable = is_connected && is_usable_network(type) | ||
| 112 | + | ||
| 113 | + if (!polling_state.network_usable) { | ||
| 114 | + stop_offline_booking_cache_polling() | ||
| 115 | + return | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + if (should_run_polling()) { | ||
| 119 | + ensure_polling_started(polling_state.last_options || {}) | ||
| 120 | + } | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + try { | ||
| 124 | + Taro.onNetworkStatusChange(polling_state.network_listener) | ||
| 125 | + } catch (e) { | ||
| 126 | + polling_state.has_network_listener = false | ||
| 127 | + polling_state.network_listener = null | ||
| 128 | + polling_state.network_usable = null | ||
| 129 | + console.error('注册网络监听失败:', e) | ||
| 130 | + } | ||
| 131 | + })() | ||
| 132 | + | ||
| 133 | + try { | ||
| 134 | + await polling_state.network_listener_promise | ||
| 135 | + } finally { | ||
| 136 | + polling_state.network_listener_promise = null | ||
| 137 | + } | ||
| 138 | +} | ||
| 139 | + | ||
| 140 | +/** | ||
| 141 | + * @description: 注销网络监听器 | ||
| 142 | + */ | ||
| 143 | + | ||
| 144 | +const teardown_network_listener = () => { | ||
| 145 | + if (!polling_state.has_network_listener) return | ||
| 146 | + if (polling_state.ref_count > 0) return | ||
| 147 | + polling_state.has_network_listener = false | ||
| 148 | + if (polling_state.network_listener && typeof Taro.offNetworkStatusChange === 'function') { | ||
| 149 | + try { | ||
| 150 | + Taro.offNetworkStatusChange(polling_state.network_listener) | ||
| 151 | + } catch (e) { | ||
| 152 | + polling_state.network_listener = null | ||
| 153 | + } | ||
| 154 | + } | ||
| 155 | + polling_state.network_listener = null | ||
| 156 | + polling_state.network_usable = null | ||
| 157 | +} | ||
| 158 | + | ||
| 159 | +/** | ||
| 26 | * @description: 启动离线预约缓存轮询 | 160 | * @description: 启动离线预约缓存轮询 |
| 27 | * @param {Object} options 选项 | 161 | * @param {Object} options 选项 |
| 28 | * @param {Number} options.interval_ms 轮询间隔,单位毫秒 | 162 | * @param {Number} options.interval_ms 轮询间隔,单位毫秒 |
| ... | @@ -30,16 +164,18 @@ const run_refresh_once = async (options) => { | ... | @@ -30,16 +164,18 @@ const run_refresh_once = async (options) => { |
| 30 | */ | 164 | */ |
| 31 | 165 | ||
| 32 | export const start_offline_booking_cache_polling = (options) => { | 166 | export const start_offline_booking_cache_polling = (options) => { |
| 167 | + normalize_options(options) | ||
| 168 | + if (!should_run_polling()) return | ||
| 33 | const interval_ms = Number(options?.interval_ms || 60000) | 169 | const interval_ms = Number(options?.interval_ms || 60000) |
| 34 | - if (polling_running) return | 170 | + if (polling_state.running) return |
| 35 | 171 | ||
| 36 | - polling_running = true | 172 | + polling_state.running = true |
| 37 | 173 | ||
| 38 | if (options?.immediate !== false) { | 174 | if (options?.immediate !== false) { |
| 39 | run_refresh_once(options) | 175 | run_refresh_once(options) |
| 40 | } | 176 | } |
| 41 | 177 | ||
| 42 | - polling_timer_id = setInterval(() => { | 178 | + polling_state.timer_id = setInterval(() => { |
| 43 | run_refresh_once(options) | 179 | run_refresh_once(options) |
| 44 | }, interval_ms) | 180 | }, interval_ms) |
| 45 | } | 181 | } |
| ... | @@ -49,13 +185,32 @@ export const start_offline_booking_cache_polling = (options) => { | ... | @@ -49,13 +185,32 @@ export const start_offline_booking_cache_polling = (options) => { |
| 49 | */ | 185 | */ |
| 50 | 186 | ||
| 51 | export const stop_offline_booking_cache_polling = () => { | 187 | export const stop_offline_booking_cache_polling = () => { |
| 52 | - if (!polling_timer_id) { | 188 | + if (polling_state.timer_id) { |
| 53 | - polling_running = false | 189 | + clearInterval(polling_state.timer_id) |
| 190 | + polling_state.timer_id = null | ||
| 191 | + } | ||
| 192 | + polling_state.running = false | ||
| 193 | +} | ||
| 194 | + | ||
| 195 | +export const enable_offline_booking_cache_polling = (options) => { | ||
| 196 | + normalize_options(options) | ||
| 197 | + if (polling_state.app_enabled) { | ||
| 198 | + ensure_network_listener().then(() => ensure_polling_started(polling_state.last_options || {})) | ||
| 54 | return | 199 | return |
| 55 | } | 200 | } |
| 56 | - clearInterval(polling_timer_id) | 201 | + polling_state.app_enabled = true |
| 57 | - polling_timer_id = null | 202 | + polling_state.ref_count += 1 |
| 58 | - polling_running = false | 203 | + ensure_network_listener().then(() => ensure_polling_started(polling_state.last_options || {})) |
| 204 | +} | ||
| 205 | + | ||
| 206 | +export const disable_offline_booking_cache_polling = () => { | ||
| 207 | + if (!polling_state.app_enabled) return | ||
| 208 | + polling_state.app_enabled = false | ||
| 209 | + polling_state.ref_count = Math.max(0, polling_state.ref_count - 1) | ||
| 210 | + if (polling_state.ref_count === 0) { | ||
| 211 | + stop_offline_booking_cache_polling() | ||
| 212 | + teardown_network_listener() | ||
| 213 | + } | ||
| 59 | } | 214 | } |
| 60 | 215 | ||
| 61 | /** | 216 | /** |
| ... | @@ -73,18 +228,19 @@ export const use_offline_booking_cache_polling = (options) => { | ... | @@ -73,18 +228,19 @@ export const use_offline_booking_cache_polling = (options) => { |
| 73 | 228 | ||
| 74 | const start = () => { | 229 | const start = () => { |
| 75 | if (!enabled) return | 230 | if (!enabled) return |
| 76 | - polling_ref_count += 1 | 231 | + polling_state.ref_count += 1 |
| 77 | - // TAG: 每次启动轮询时,都增加引用计数 | 232 | + ensure_network_listener().then(() => { |
| 78 | - start_offline_booking_cache_polling(options) | 233 | + ensure_polling_started(options) |
| 234 | + }) | ||
| 79 | is_running.value = true | 235 | is_running.value = true |
| 80 | } | 236 | } |
| 81 | 237 | ||
| 82 | const stop = () => { | 238 | const stop = () => { |
| 83 | if (!is_running.value) return | 239 | if (!is_running.value) return |
| 84 | - polling_ref_count = Math.max(0, polling_ref_count - 1) | 240 | + polling_state.ref_count = Math.max(0, polling_state.ref_count - 1) |
| 85 | - // TAG: 每次停止轮询时,都减少引用计数 | 241 | + if (polling_state.ref_count === 0) { |
| 86 | - if (polling_ref_count === 0) { | ||
| 87 | stop_offline_booking_cache_polling() | 242 | stop_offline_booking_cache_polling() |
| 243 | + teardown_network_listener() | ||
| 88 | } | 244 | } |
| 89 | is_running.value = false | 245 | is_running.value = false |
| 90 | } | 246 | } | ... | ... |
-
Please register or login to post a comment