hookehuyr

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

- 将分散的状态变量整合为 polling_state 对象
- 增加网络状态监听功能,仅在网络可用时运行轮询
- 新增 enable/disable 方法管理轮询生命周期
- 优化选项处理和状态管理逻辑
...@@ -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 }
......