前段时间完整的看了云谦大佬分享的「前端框架的趋势与实践」的视频。看到有关数据流的请求方案即数据流满足大量CRUD场景趋势的时候,我注意到云谦大佬提到了一个关键词------乐观更新。
抱着求知的态度进行自我探索,我特意进行了一番Google,发现有关乐观更新的文章寥寥无几,而且看完之后依然"云深不知处",所以我打算将我对于乐观更新的学习心得分享出来,希望能对感兴趣的小伙伴有所帮助。
乐观更新是什么
我们进行页面渲染的时候,通常是进入页面先发起请求从服务端获取相应的数据,然后根据服务端返回响应数据之后再去进行视图更新。而在请求的过程中,需要消耗一定的时间来等待服务端的响应,为了防止用户跳出,我们通常会给用户一定的反馈,保证用户知道当前数据正在处理中。为了给用户提供更好的体验,比较常见的做法就是给用户显示一个loading的菊花图,表示当前正在数据请求中。
而乐观更新正式对这个环节继续了调整,追求更快的渲染速度,当请求发起之后,直接按照心里预期进行页面渲染,而不等待请求的完成。
乐观更新的分析
乐观更新虽然有助于提升用户体验,但是我们绝对不能盲目乐观。我们要对乐观更新有清醒的认识,然后在正确的场景运用它,充分发挥乐观更新的优势。 如果错误的使用了乐观更新的话,不但起不到提升用户体验的效果,反而会降低用户的粘性。
乐观更新的优势
乐观更新可以大幅提升用户体验,并在第一时间与用户进行交互,可以增加用户的粘性。
乐观更新的劣势
- 乐观更新对网络请求的成功性有要求,需要保证请求的失败率极低;
- 乐光更新使用场景有限制,更偏向于展示性交互或者单一性操作;
- 乐观更新需要可逆,如果请求异常之后,需要进行及时修正;
乐观更新的应用场景
基于乐观更新的特性,对比较适合使用乐观更新的应用场景进行了分析,找到了几个比较适合的场景:
- 论坛类数据
- 聊天消息的发送、撤销、删除
- 列表类数据的增删查改
我们进行乐观更新的时候,是基于请求大概率成功的前提下,所以说乐观更新存在着失败的可能性(虽然可能性非常低)。当请求异常时,我们需要重新进行获取数据,对现有乐观更新的数据进行修复;如果重新获取不到的话,我们就要对数据进行回滚操作。
乐观更新的伪代码
我们使用微信发送消息的时候,消息发送之后,会第一时间显示在客户端;如果消息发送失败,微信会将消息的状态更新为发送失败状态;如果消息发送成功,客户端没有变化。我们就基于这样的场景,通过伪代码来实现一下消息的乐观更新,辅助理解一下。
我们先来实现一下发送成功的交互,当用户点击发送之后,我们进行乐观更新消息内容,这里我们并不会使用async await
或者 Promise
对消息发送请求进行等待。
JavaScript
function sendMsg () {
// 获取当前数据的上下文(伪代码)
const ctx = getContext();
// 保存历史数据的快照
const previousMsgs = ctx.getMsgList();
// 发送消息,异步处理
ctx.sendMsg()
.then((err) => {
if (err) {
ctx.rollback(err, previousMsgs);
} else {
ctx.commit(newMsg);
}
});
ctx.updateMsgList((msgList) => ([...msgList, newMsg]));
return { previousMsgs }
}
前面我们说了,乐观更新存在着一定的失败几率,所以我们需要实现一个容错机制,用来做消息的查询或者回滚,保证我们请求异常时能够将数据修正。
JavaScript
function handleError(previousMsg) {
try {
// 重新进行数据查询
const msgList = getMsgList();
this.updateMsgList(msgList);
} catch (error) {
// 查询失败,直接进行回滚
this.updateMsgList(previousMsgs);
}
}
我们将两步组合到一起,就是乐观更新的伪代码。
JavaScript
useOptimistic(msgList, sendMsg, handleError);
但是,还有一个很重要的问题那就是更新时序问题。
使用过React的小伙伴,应该知道React在进行setState的时候,也存在着时序的问题,只不过React采用的解决方案是回调,回调中接受上一个状态。
对于我们乐观更新也存在着同样的问题。我们进行前后端数据通讯的时候,响应的先后顺序取决于多个因素,但绝对不是完全按照发送的先后顺序依次响应的。那这也就意味着,我们对于同一数据进行操作时,存在先后顺序异常的情况。所以我们需要对同一数据的操作进行干预,以防止数据最终状态不是我们期望的。
一种方式是同一时间只允许一个操作任务进行(不可取,满足不了实际需求)
另外一种方式是,我们实现一个更新队列(先进先出的机制),在操作的时候将任务推入到更新队列中,等待前一个任务出队之后再执行下一个任务,直到任务完全出队。
小结
虽然,现在没有现成的轮子来实现乐观更新,但是我相信,随着大家对用户体验的极致追求,乐观更新会普及到更多的业务开发过程中。虽然实现起来有一定的技术难度,但是带来的产品价值也是十分可观的。
最后,希望上面的内容对大家有所帮助。