React.js中基于BLE的延迟更新:如何仅发送最新请求?
问题:BLE设备的延迟更新(仅发送最新操作)
我正在使用基于浏览器的BLE技术,该技术同一时间仅允许执行一次更新操作。我希望在用户操作后尽快向设备发送更新,但仅发送两次更新调用之间的最后一次更新(即不排队逐个发送)。
举个例子:如果用户快速点击按钮多次,我希望尽快发送更新,但必须等上一次更新完成后才能发下一次,同时不想用“正在发送”的模态框阻止用户操作。
我写了一段示例代码来演示问题(现有代码存在冲突,里面的useEffect是用来确保发送最终状态的临时方案):
import React from "react"; import ReactDOM from "react-dom"; import { useState, useEffect } from "react"; import "./styles.css"; // 模拟BLE调用 window.sending = false; window.testSend = (n) => { if (window.sending) { throw new Error("Busy"); } window.sending = true; console.log("DataSent: " + n); return new Promise((resolve) => setTimeout(() => { window.sending = false; resolve(); }, 1000) ); }; function App() { const [count, setCount] = useState(0); const [sendingData, setSendingData] = useState(false); // 临时方案 useEffect(() => { const id = setTimeout(() => { window.testSend(count).then(() => setSendingData(false)); }, 2000); return () => clearTimeout(id); }, [sendingData]); function inc() { setCount(count + 1); sendDataSlowly(count + 1); } function sendDataSlowly(newData) { if (sendingData) { return; } setSendingData(true); window.testSend(newData).then(() => setSendingData(false)); } return ( <div className="App"> <button onClick={inc}>Click Me {count}</button> </div> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);
有没有实现这类延迟更新的标准模式?我试过在sendDataSlowly里加“LastMissed”状态,但总是没法保证发送正确的最新数据,还容易陷入循环。
标准解决方案:跟踪最新待发送值 + 自动重试机制
核心思路是维护两个状态:
sendingData:标记当前是否正在执行BLE发送操作pendingData:保存最新的待发送数据,如果当前正在发送,新的操作只会更新这个值,不会触发重复发送
当当前发送完成后,自动检查是否有未发送的最新数据,如果有就立即发送,这样就能保证只发送每次操作间隙的最后一次更新。
修改后的完整代码如下:
import React from "react"; import ReactDOM from "react-dom"; import { useState, useEffect } from "react"; import "./styles.css"; // 模拟BLE调用 window.sending = false; window.testSend = (n) => { if (window.sending) { throw new Error("Busy"); } window.sending = true; console.log("DataSent: " + n); return new Promise((resolve) => setTimeout(() => { window.sending = false; resolve(); }, 1000) ); }; function App() { const [count, setCount] = useState(0); const [sendingData, setSendingData] = useState(false); // 新增:保存最新的待发送数据,null表示无待发送 const [pendingData, setPendingData] = useState(null); // 核心逻辑:当不在发送且有待发送数据时,触发发送 useEffect(() => { if (!sendingData && pendingData !== null) { setSendingData(true); window.testSend(pendingData) .then(() => { // 发送完成后,清空当前pending数据(因为已经发送了最新的) setPendingData(null); setSendingData(false); }) .catch((err) => { // 发送失败的话,可以根据需求处理,比如重试或者清空pending console.error("发送失败:", err); setSendingData(false); }); } }, [sendingData, pendingData]); function inc() { const newCount = count + 1; setCount(newCount); // 只更新最新的待发送数据,不管当前是否在发送 setPendingData(newCount); } return ( <div className="App"> <button onClick={inc}>Click Me {count}</button> </div> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);
关键流程说明:
- 用户点击按钮时,只更新
count和pendingData为最新值,不直接触发发送 useEffect会监听sendingData和pendingData的变化:- 如果当前没有在发送,且存在待发送数据,就立即启动发送
- 发送过程中,用户的新操作只会更新
pendingData,不会打断当前发送
- 发送完成后,清空
pendingData并标记发送结束,此时如果有新的pendingData(用户在发送过程中点了多次),useEffect会自动触发下一次发送,且发送的是最新的那个值
这种模式完全避免了手动排队的问题,也不会丢失用户的最新操作,同时不需要阻止用户交互。
内容的提问来源于stack exchange,提问作者JamesWilliams
相关产品推荐
相关产品推荐

