您将如何在RxJS中实现乐观更新?请参阅以下示例:

const todoIdSource: Subject<string> = new Subject();
const $todo = todoIdSource.pipe(
    tap(id => {
        const optimisticTodo: any = {
            userId: 1,
            id: id, // Use some value from the source observable
            title: "delectus aut autem",
            completed: false
        };
        // TODO force emit optimisticTodo! How? :)
    }),
    concatMap((id) => ajax('https://jsonplaceholder.typicode.com/todos/' + id)),
    map(response => response.response)
);

$todo.subscribe(value => console.log('TODO', value));

todoIdSource.next('1');


在ajax调用完成之前,$todo不会发出。
但是我希望它在todoIdSource发出后立即发出乐观值。
最后,$ todo应该发出2个值:


乐观价值
来自ajax结果的值


注意:


乐观值不应触发ajax调用。
乐观值应该能够使用来自可观察的todoIdSource源的数据。


在RxJS中是否有一种完美的方法来完成此任务?我可以想象引入第二个Observable optimisticTodo$,然后将其与$todo合并。但这并不优雅:)

最佳答案

要么

concatMap(id =>
    from(ajax("https://jsonplaceholder.typicode.com/todos/" + id)).pipe(
      map(response => response.response),
      startWith({...})
    )
  )


请记住,startWith必须是最后一个运算符(或至少在map之后)。
这样可以确保传递给您的值不会被映射,在这种情况下,由于您使用映射来提取响应,因此不会被映射。

要么

  concatMap(id =>
    merge(
      of({...}),
      from(ajax("https://jsonplaceholder.typicode.com/todos/" + id)).pipe(
        map(response => response.response)
      )
    )
  )


两者都实现了相同的目的:

每当todoIdSource发出时,立即发出乐观的todo,并在ajax调用结束时发出实际的todo。

我更喜欢startWith方法。

stackblitz

小附录:我假设订阅者实际上只对当前状态感兴趣。

您可以添加另一个

  scan((acc,todo) =>({...acc,...{[todo.id]:todo}}), {}),
  map(v => Object.keys(v).map(k => v[k])),


为此,请参见addendum stackblitz

09-26 23:35