首页 > 解决方案 > 在我订阅 observable 之前,异步管道不会收到值

问题描述

我遇到了一个奇怪的情况,当我订阅 observable 之前异步管道无法正常工作。资料来源:

组件视图:

<div *ngIf="loading$ | async" fxLayout="row" fxLayoutAlign="center">
    <span class="spinner">Loading...</span>
</div>

<h1>{{data$ | async}}</h1>

零件:

@Component({
  selector: './tm-fake',
  changeDetection: ChangeDetectionStrategy.OnPush,
  templateUrl: './fake.component.html'
})
export class FakeComponent implements OnInit {
  constructor(
    private _fs: FakeService
  ) {}

  loading$ = new BehaviorSubject<boolean>(false);
  data$ = this._fs.data$.pipe(indicateUntilNULL(this.loading$));

  ngOnInit(): void {
    this._fs.requestData();

    //this.data$.subscribe(e => {});
  }
}

服务:

@Injectable()
export class FakeService {
  requestData(): void {
    timer(2000).pipe(map(e => "data")).subscribe(e => this._data.next(e));
  }

  private readonly _data = new BehaviorSubject<string>(null);
  readonly data$ = this._data.asObservable();
}

操作员:

export function indicateUntilNULL<T>(indicator: BehaviorSubject<boolean>): (source: Observable<T>) => Observable<T> {
  return source => source.pipe(
    tap(e => indicator.next(!e))
  );
}

我看到预期的data2 秒后延迟,但在此延迟期间我没有看到微调器。但是,如果我取消注释该行this.data$.subscribe(e => {});,它就会开始正常工作。而且我找不到原因。有任何想法吗?

注意:loading$即使注释了这一行,也会发出正确的值。

解决方案

非常感谢@theMayer 和他的有用建议。我只是想添加文章,其中详细解释了问题。根据这篇文章,最优雅的解决方案是delay(0)运算符:

export function indicateUntilNULL<T>(indicator: BehaviorSubject<boolean>): (source: Observable<T>) => Observable<T> {
  return source => source.pipe(
    delay(0),
    tap(e => indicator.next(!e))
  );
}

标签: angularrxjs

解决方案


好的,在查看您的代码后,我想我理解了这个问题。您没有看到通过DOM 中拾取true的 observable 发出的更新值。loading$

这是您的代码所说的将发生的情况:

  1. 对象将被构造,行为主体_data将被初始化为空值。

  2. Angular 模板将呈现并通过更改检测。它将falseloadingvalue 和nulldata. 装载微调器不会旋转。

  3. 在更改检测周期的后期,async管道将触发true要由 发出的值loading。这是同步发生的;由于更改检测设置为OnPush,我怀疑此更改将被错过,因为在设置值时已经有一个更改检测周期正在进行中(通常,这可能会被常规更改检测策略捕获并作为错误引发) .

  4. 然后你data会在两秒延迟后发出。

请注意,当使用常规更改检测时,这种情况将导致ExpressionChangedAfterItHasBeenCheckedError. 正确的方法是在变更检测周期之前确保组件的内部一致性,而不是在变更检测期间执行会触发进一步变更的事情。

快速的答案是最初将“加载”值设置为 true,因为代码在初始化时总是会尝试加载数据。


推荐阅读