【问题标题】:How to create a method that returns Observable that emits result of 2 Promises that need to be executed one after another?如何创建一个返回 Observable 的方法,该方法发出需要一个接一个执行的 2 个 Promise 的结果?
【发布时间】:2021-12-30 01:13:12
【问题描述】:

我问了一个问题

Is Observable from chained promises equivalent of observables created with from and chained with concatMap?

在完全错误的前提下。看来我的两个解决方案都与我的意图无关。

我创建了一个返回 Observable 的方法,并调用了两个返回 Promise 的方法。我尝试了两种方法:

  public setItemInfos(itemInfos: IItemInfo[]): Observable<number> {
    return from(this.db.selectionItemInfos.clear().then(() => {
      return this.db.selectionItemInfos.bulkAdd(itemInfos);
    }));
  }

  public setItemInfos(itemInfos: IItemInfo[]): Observable<number> {
    const clear$ = from(this.db.selectionItemInfos.clear());
    const bulkAdd$ = from(this.db.selectionItemInfos.bulkAdd(itemInfos));

    return clear$.pipe(concatMap(() => bulkAdd$))
  }

用途将是:

myService.setItemInfos(itemInfos).subsribe(count => {
  console.log(`Cleared the table 1st and then added ${count} new items`);
});

我从两个版本中都认为:

  1. table clear 是在 bulkAdd 开始时执行完成
  2. bulkAdd 完成后,我从订阅中获取计数

这应该怎么做?或者可以做到吗?

【问题讨论】:

  • 你有什么特定的理由想要将 Promise 包装到 observables 中吗?像这样的命令式操作通常使用异步函数(带有等待)更容易组合。我很好奇 Observables 在这种情况下给你带来了什么好处。另一方面,在查询数据(从 db 读取)时,Observables 非常适合观察只读查询,这就是 Dexie 3.2 使用其新的 liveQuery() 函数内置它们的原因。
  • 不幸的是,我是 Promise 文盲,恕我直言,试图将所有这些 async/await 代码添加到纯粹在 Observables 上运行的应用程序并不容易。
  • @DavidFahlander:我会很感激一个例子,这是如何使用异步等待完成的。

标签: promise rxjs indexeddb dexie


【解决方案1】:

这是(据我所知),我会怎么做。

一般来说,defer(或任何高阶运算符)是从 Promise 创建 observable 的更好方法。 Defer 让您可以将 Promise 的热切求值语义转化为可观察对象的惰性求值语义。

然后所有常用的可观察运算符等都会按预期运行。

public setItemInfos(itemInfos: IItemInfo[]): Observable<number> {
  const clear$ = defer(() => this.db.selectionItemInfos.clear());
  const bulkAdd$ = defer(() => this.db.selectionItemInfos.bulkAdd(itemInfos));

  return concat(clear$, bulkAdd$);
}

更新 1:

所以我想我可能知道你在追求什么。这并不是真正地道的 RxJS,因为它是声明式、命令式代码风格的交错组合。即便如此,这应该有效吗?我还没有完全测试它,但是有些修修补补,我认为这应该可以满足您的需求。

肯定有更好的方法来完成同样的事情,但是如果没有看到你所追求的大局,很难说。


interface Tagged<T> {
  payload: T,
  tag: number
}

class abitraryClass{

  private setItemInfoSub: Subject<Tagged<IItemInfo[]>>;
  private processItemInfo: Observable<Tagged<number>>;
  private itemInfoTag = 0;

  constructor(){
    this.setItemInfoSub = new Subject<Tagged<IItemInfo[]>>();
    this.processItemInfo = this.setItemInfoSub.pipe(
      concatMap(({tag, payload: itemInfos}) => this.db.selectionItemInfos.clear().pipe(
        ignoreElements(),
        concatWith(defer(() => this.db.selectionItemInfos.bulkAdd(itemInfos))),
        map(response => ({
          payload: response,
          tag
        }))
      )),
      shareReplay(1)
    );
    // Make the processing pipeline live at all times.
    this.processItemInfo.subscribe();
  }

  public setItemInfos(itemInfos: IItemInfo[]): Observable<number> {
    const myTag = this.itemInfoTag++;

    this.setItemInfoSub.next({
      payload: itemInfos,
      tag: myTag
    });

    return this.processItemInfo.pipe(
      filter(({tag}) => tag == myTag),
      map(({payload}) => payload)
    );
  }
}

【讨论】:

  • 感谢您的回答!我实际上尝试过与 defet 类似的 ealier,但它是 clear$pipe(concapMap(() =&gt; bulkAdd$) 的问题是,当它在可能完成之前被多次调用时它被混淆了。在那个版本中,错误来得非常快。不过我试试这个
  • 我知道我的问题并没有说明它还需要与多个快速调用一起使用,所以即使它不能解决我的问题,这也可能是合格的答案
  • concat 也被弃用并且concat(clear$, bulkAdd$) 的返回类型是 Observable 所以它不会构建。
  • 我仍然不认为这正是您想要的。 concat(clear$, bulkAdd$) 将从所有输入的 observable 中发出值,而您只需要第二个。这是一个stackblitz 来说明。打开控制台看看发生了什么。
  • @charm 所以我做了一个更新,它没有经过彻底的测试,但你可以尝试一下并修改一下。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多