Optimistic UI指当前端进行一个异步(如向服务器发送请求)过程时,不等过程结束就响应界面,典型的如邮箱应用中点击“删除邮件”时,邮件会立刻消失,随后才请求服务器进行真正的删除操作。
Optimistic UI的大致过程为:
- 发起异步操作
- 预测操作的结果,并进行界面更新
- 待异步操作结束后,对界面进行调整
在一个复杂的系统中,异步操作会存在并行和乱序的问题,此时多个“预测操作结果更新界面”和“异步完成后调整界面”以随机的顺序进行,容易导致界面出现错误,因此需要一种机制进行管理。
在本文涉及的策略中,界面通过一个称为State的对象生成,即State相同时,界面一定相同。本文的核心思想是将“异步操作”(后称Promise)和“计算State”(后称Action)分离,难过队列的形式管理State的变化,通过找回旧的State来实现异步结束后的界面调整的同时不引起界面错乱。
每个事件处理函数返回一个[Promise<Action>, Action],前者负责真正更新状态,后者负责产生Optimistic State。
每次应用Optimistic State的时候,记录以下内容:
- 标记当前State是Optimistic的
- 产生这个Optimistic State的Action
- 与这个Optimistic State相关的Promise
- 最后一个正式的State
比如有以下顺序:
-
初始状态state0,此时队列为
[state0] -
开始事件1,产生
[Promise<Action11>, Action12] -
应用Action12,此时队列为:
[ state0, {state1: Action12(state0), Promise<Action11>, Action12, pending: true} ]使用state1更新UI
-
开始事件2,产生
[Promise<Action21>, Action22] -
取最后的Optimistic State即state1应用Action22,此时队列为:
[ state0, {state1: Action12(state0), Promise<Action11>, Action12, pending: true}, {state2: Action22(state1), Promise<Action21>, Action22, pending: true} ]使用state2更新UI
-
假设事件2比事件1更早结束,则获取到Action21,使用在队列中Action21所在项之前的最后的Optimistic State即state1并应用Action21,但同时还要保留Action21(因为前面还有未完成的事件,这个状态并不是真正的最终状态),此时队列为:
[ state0, {state1: Action12(state0), Promise<Action11>, Action12, pending: true}, {state3: Action21(state1), Promise<Action21>, Action22, pending: false} // 这里变成false了 ]使用state3更新UI
-
事件1结束,获取到Action11,使用队列中Action11所在项之前最后的Optimistic State,此时没有这样的状态,因此不做任何操作,把Action11相关的项的pending票房为false
[ state0 {state1: Action12(state0), Promise<Action11>, Action12, pending: false}, // 这里也变成false了 {state3: Action21(state1), Promise<Action21>, Action22, pending: false} // 这里变成false了 ] -
当有第7步(找不到最近的Optimistic State)时,在队列中从头到尾找所有pending为false的,一个一个应用到正式的State上,直到碰到pending为true的停下来,则队列会变成:
[ state4: Action21(Action11(state0)) ]将state4作为正式State并更新UI
你好,想问下关于异步操作失败时的 Action 是否也是在
Promise<Action>里了?