管道 rxjs 运算符如何可能受到后续运算符返回值的影响?

How is a piped rxjs operator possibly affected by a subsequent operator's returned value?

为了检查 rxjs 管道中运算符之间的中间值,我尝试使用 tap 将它们简单地记录到控制台。

我有两个水龙头,一个在用于对数组进行排序的映射运算符之前,一个在它之后。令我惊讶的是,对于两次点击,记录了相同的排序数组,而我希望首先看到原始顺序,然后是排序的数组

因此 map 运算符中的排序会影响之前的 tap 运算符使用的 observable。尽管如此,操作员的功能似乎如预期的那样一个接一个地执行,因为第二张地图的日志记录在水龙头的日志之间。

我的代码(我打算稍后用合适的类型替换 Observable 数组的类型):

public getJSONData(): Observable<any[]> {
    return this.http.get(this.poisJSONPath).pipe(
      map((featureCollection: any) => featureCollection.features),
      tap((features) => console.log(features)),
      map((features: any) => {
        console.log('this is logged in between as expected');
        return features.sort((a, b) =>
          a.properties.name > b.properties.name ? 1 : -1
        );
      }),
      tap((features) => console.log(features))
    );
  }

我假设我缺少 rxjs 管道操作模式的一些基本特征。在我发现的所有其他示例中,水龙头似乎按我的预期工作。我在这里错过了什么?

最简单(但不仅如此)的答案是数组已经排序。


进一步调查,当我 运行 这个:

of({
  features: [3,4,7,1,3,8]
}).pipe(
  pluck('features'),
  tap(console.log),
  map(features => {
    console.log('this is logged in between as expected');
    return features.sort(
      (a, b) => a > b ? 1 : -1
    );
  }),
  tap(console.log)
).subscribe(console.log);

这是我的输出:

[3,4,7,1,3,8]
this is logged in between as expected
[1,3,3,4,7,8]
[1,3,3,4,7,8]

所以,这可能与您 运行 所在的环境有关。

例如,如果 console.log 被缓冲 - 那么即使保留打印顺序,数组在打印之前可能已经在内存中更改。


考虑

RxJS 不对内存中的内容做出任何保证。这就是为什么我们鼓励您使用纯的、不可变的函数。让你的程序逻辑更容易推理。

考虑一下:

const delayedLog = val => setTimeout(() => console.log(val), 1000);

of({a: 5}).pipe(
  tap(delayedLog),
  map(input => {
    delayedLog("this is logged in between as expected");
    input.a++;
    return input;
  }),
).subscribe(delayedLog);

输出:

{"a":6}
this is logged in between as expected
{"a":6}

{"a":6} 被打印两次,即使在调用第一个延迟日志时 a = 5。这是因为在控制台从内存中读取值之前内存中的值发生了变化。

VS

const delayedLog = val => setTimeout(() => console.log(val), 1000);

of({a: 5}).pipe(
  tap(delayedLog),
  map(input => {
    delayedLog("this is logged in between as expected");
    return { a: input.a + 1 }
  }),
).subscribe(delayedLog);
{"a":5}
this is logged in between as expected
{"a":6}

这里我们得到了预期的输出,因为我们没有改变我们的对象,我们 return 一个具有增量值的新对象。


开始寻找的地方

尝试对数组的浅表副本进行排序,看看是否可以解决问题。

public getJSONData(): Observable<any[]> {
  return this.http.get(this.poisJSONPath).pipe(
    pluck('features'),
    tap(console.log),
    map((features: any[]) => {
      console.log('this is logged in between as expected');
      return [...features].sort((a, b) =>
        a.properties.name > b.properties.name ? 1 : -1
      );
    }),
    tap(console.log)
  );
}