RXJS 6 - toPromise - PullRequest
       27

RXJS 6 - toPromise

0 голосов
/ 31 октября 2018

Я сейчас пытаюсь преобразовать Observable в Обещание. Но когда я вызываю этот метод, ничего не происходит. Я использую Angular 6.

Услуги

  public create(form: StoryForm): Promise<void | string> {
    const key: string = this.afStore.createId();

    return this.auth.authState.pipe(map(res =>
      <Story>{
        title: form.title, content: form.content, createdAt: new Date(), sid: key,
        uid: res.uid, username: res.displayName
      }
    )).toPromise().then((story: Story) =>
      this.afStore.doc(`stories/${key}`).set(story).catch(err => err.message));
  }

Компонент

  public save() {
    this.triedToSave = true;
    if (this.storyForm.valid) {
      this.storyService.create(this.storyForm.value)
        .then(() => this.router.navigate(['/stories']))
        .catch((err: string) => this.notify.danger(err));
    }
  }

Что нужно сделать, это сохранить или хотя бы отобразить ошибку.

Auth

Как реализовано authstate: он возвращает наблюдаемую часть пользовательской информации. Он реализован в другом сервисе и выглядит так:

  public get authState(): Observable<firebase.User> {
    return this.afAuth.authState;
  }

Редактировать

Что меня смущает, так это то, что если я использую фиктивный объект, он неожиданно работает:

  public create(form: StoryForm) {
    const key: string = this.afStore.createId();

    return of({uid: 'blubb', displayName: 'kdsjf', photoUrl: 'kjdfkjfd'}).pipe(map(user => {
      return {
        title: form.title, content: form.content, createdAt: new Date(), sid: key,
        uid: user.uid, username: user.displayName, photoUrl: user.photoURL
      } as Story;
    })).toPromise();
  }

Но мне интересно, почему toPromise не работает на примере выше ...

1 Ответ

0 голосов
/ 06 ноября 2018

Я предполагаю, что ничего не происходит, потому что когда вы запускаете метод save, из authState ничего не выходит. Очевидно, вы ожидаете, что наблюдаемое authState или Subject всегда будет вызывать какой-либо вывод, что имеет место только в определенных случаях.

Код ниже создает -new- наблюдаемый, который слушает authState.

return this.auth.authState.pipe(map(res =>
      <Story>{
        title: form.title, content: form.content, createdAt: new Date(), sid: key,
        uid: res.uid, username: res.displayName
      }
    )).toPromise().then((story: Story) =>
      this.afStore.doc(`stories/${key}`).set(story).catch(err => err.message));

Этот код запускается только методом сохранения. Я предполагаю, что authState является либо наблюдаемым, либо предметом. Ваш код будет работать только тогда, когда authState передается новое значение -after- метод save запущен.

Ваш код, который использует фиктивный объект, работает, потому что вы создаете наблюдаемую, которая сразу же генерирует это одно значение.

Если объект authState: заменить его на ReplaySubject (1)

Если это наблюдаемая, вам нужно опубликовать ее как ReplaySubject, например:

authState.pipe(
    publishReplay(1),
    refCount()
);

Чтобы полностью понять, что происходит, прочитайте эту статью: https://blog.mindorks.com/understanding-rxjava-subject-publish-replay-behavior-and-async-subject-224d663d452f

Это Java-статья, но применяются те же принципы.

Но, честно говоря, я съеживаюсь, когда вижу, что люди используют метод toPromise Вы бы выучили rxjs намного быстрее, если бы использовали его по назначению!

Если бы я написал этот код, он бы выглядел примерно так:

public save$: Subject<StoryForm> = Subject<StoryForm>();
private destroy$: Subject<any> = new Subject();

ngOnDestroy(): void {
    this.destroy$.next();
}

onInit() {
    // the (click) eventhandler in your GUI should call save$.next(storyForm)
    // this will trigger this statement
    this.save$.pipe(
        // withLatestFrom will fetch the last value from an observable, 
        // it still needs to be a ReplaySubject or ReplaySubject for this to work though!
        // it will pass an array down the pipe with the storyForm value, and the last value from authState
        withLatestFrom(this.auth.authState),
        // switchMap does the actual work: note that we do not return a value, 
        // but an observable that should that return a value soon, that is why we need switchMap!
        switchMap(([storyForm, authInfo]) => {
            // i am assuming the "set" method returns an observable
            // if it returns a Promise, convert it using fromPromise
            return this.afStore.doc(`stories/${key}`).set(story).pipe(
                // catchError needs to be on your api call
                catchError(err => console.log(err))
            );
        }),
        // this will kill your subscriptino when the screen dies
        takeUntil(this.destroy$)
    ).subscribe(value => {
        // "value" will be the return value from the "set" call
        this.router.navigate(['/stories']);
    }
}
...