Не удается получить результат первого HTTP-запроса с помощью switchMap RxJS

Я пытаюсь сделать 2 HTTP-запроса и в первом вызове пытаюсь создать запись, а затем по ее результатам (ответ от метода API) хочу выполнить или пропустить второй вызов, который обновляет другие данные. Однако, хотя я могу поймать ошибку в блоке catchError, я не могу получить ответ в методе switchMap первого вызова. Итак, что не так с этой реализацией в соответствии с данным сценарием? И как я могу получить ответ первого результата и продолжить или нет второй вызов в соответствии с этим первым ответом?

let result;
let statusCode;

this.demoService.create(...).pipe(
    catchError((err: any) => { ... }),
    switchMap(response => {

    // I am trying to get the response of first request at here
    statusCode = response.statusCode;

    if(...){
        return this.demoService.update(...).pipe(
            catchError((err: any) => { ... }),
            map(response => {
            return {
                result: response
              }
          }
        )
      )}
    }
  ))
  .subscribe(result => console.log(result));

person Prag Dopravy    schedule 15.11.2020    source источник
comment
Можете ли вы попробовать добавить tap(console.log) в свой канал перед catchError, просто чтобы подтвердить, возвращает ли служба create данные и регистрирует ли что-нибудь? tap импортируется под rxjs/operators   -  person KShewengger    schedule 15.11.2020
comment
Что именно вы подразумеваете под я не могу получить ответ в switchMap? Вы не можете прочитать свойство statusCode? Выдает какую-то ошибку? Вы уверены, что свойство доступно в ответе? Пожалуйста, дополните.   -  person Michael D    schedule 15.11.2020
comment
@MichaelD Что я хочу сделать, так это получить ответ на первый вызов (создать) и в соответствии с его результатом выполнить второй вызов (обновление) или сломать (если есть ошибка, я думаю, что switchMap не выполняет другие, но если ошибки нет, я могу захотеть сломаться в соответствии со значениями ответа). Конечно, мне также нужно получить ответ на второй звонок.   -  person Prag Dopravy    schedule 15.11.2020
comment
@PragDopravy: А что именно ты сейчас не можешь сделать?   -  person Michael D    schedule 15.11.2020
comment
@KShewengger Я пробовал tap(response => { console.log(response); debugger; }),, но код не попал туда и попал в блок switchMap (после этого он все еще не попал в блок tap()). Итак, есть ли ошибка в моем использовании?   -  person Prag Dopravy    schedule 15.11.2020
comment
@KShewengger Просто исправление: когда ошибки нет, она попадает в tap, но ответ равен нулю, даже если есть ответ от первого вызова.   -  person Prag Dopravy    schedule 15.11.2020
comment
@MichaelD Если есть ошибка в первом вызове, я могу получить ее в первом блоке catchError. Если ошибки нет, я не могу получить ответ (это будет код состояния и т. д.) в блоке tap и т. д.   -  person Prag Dopravy    schedule 15.11.2020
comment
Привет @PragDopravy, tap на самом деле является платформой ведения журналов в rxjs, где вы можете отлаживать вещи, не влияя на ваш ответ, поэтому мы попытались поместить туда console.log, если служба действительно что-то возвращает. Если внутри этого крана нет журнала, это означает, что в первую очередь ваш this.demoService.create(...) ничего не вернул для обработки. Гораздо лучше, если вы можете проверить это вместо этого, надеюсь, это поможет   -  person KShewengger    schedule 15.11.2020
comment
@KShewengger Спасибо за помощь, да, это хорошая идея. С другой стороны, могу ли я использовать тап, чтобы проверить результат, а затем продолжить или не выполнять следующий вызов (обновление)? Если да, то как мне использовать его в блоках if или вне их?   -  person Prag Dopravy    schedule 15.11.2020
comment
@MichaelD У вас есть предложения для этого сценария?   -  person Prag Dopravy    schedule 15.11.2020
comment
@PragDopravy, что касается проверки результата, да, но если это то, что вы будете использовать, чтобы не выполнять следующий вызов, боюсь, это невозможно. Вместо этого как насчет использования rxjs iif? Дает вам условие if, что если это условие выполняется, вы выполняете этот вызов, если нет, он выполняет другой. Вы можете проверить документы здесь: learnrxjs.io/learn-rxjs/operators/conditional /iif надеюсь, это поможет :)   -  person KShewengger    schedule 15.11.2020
comment
Является ли этот подход является лучшим вариантом для отправки нескольких HTTP-запросов и выполнения следующего запроса на основе условия? Я не уверен, что существует более элегантное решение с использованием switchMap и т.д. Любое предложение будет оценено.   -  person Prag Dopravy    schedule 15.11.2020
comment
Ваш вопрос все еще не ясен. В одном из комментариев говорится нажмите switchMap блок, а в другом комментарии говорится Я не могу получить ответ.   -  person Michael D    schedule 15.11.2020
comment
На самом деле я искал iif, но ничего не мог понять в документах. У вас есть идеи, как применить это к моему сценарию в моем вопросе? Не могли бы вы применить и опубликовать код в качестве ответа?   -  person Prag Dopravy    schedule 15.11.2020
comment
@MichaelD Я просто хочу сделать это: сначала отправьте первый запрос, а затем получите его результат в условии if. Затем, исходя из этого условия, я хочу выполнить второй вызов HTTP или пропустить его и отобразить сообщение. Итак, как мне отредактировать свой код, чтобы использовать его с элегантным подходом?   -  person Prag Dopravy    schedule 15.11.2020
comment
Привет @PragDopravy, отправил ответ. Надеюсь, поможет :)   -  person KShewengger    schedule 15.11.2020


Ответы (2)


Вопрос для меня до сих пор не ясен. Я опубликую более общий ответ, чтобы прояснить некоторые вещи

Есть несколько вещей, которые следует отметить

  1. Когда наблюдаемый объект выдает уведомление error, наблюдаемый объект считается закрытым (если только он не будет запущен снова), и ни один из следующих операторов, зависящих от уведомлений next, не будет запущен. Если вы хотите перехватывать уведомления об ошибках внутри файла switchMap, вы можете вернуть уведомление next из файла catchError. Что-то вроде catchError(error => of(error)) с использованием функции RxJS of. Затем уведомление будет перехвачено следующим switchMap.

  2. Вы должны вернуть наблюдаемое из switchMap независимо от вашего состояния. В этом случае, если вы не хотите ничего возвращать, когда условие не выполняется, вы можете вернуть RxJS NEVER. Однако, если вы хотите отправить сообщение, которое может быть перехвачено обратным вызовом подписки next, вы можете использовать функцию RxJS of. Замените return NEVER на return of('Some message that will be emitted to subscription's next callback');

import { of, NEVER } from 'rxjs';
import { switchMap, catchError, map } from 'rxjs/operators';

this.demoService.create(...).pipe(
  catchError((err: any) => { ... }),
  switchMap(response => {
    statusCode = response.statusCode;

    if (someCondition) {
      return this.demoService.update(...).pipe(  // emit `update()` when `someCondition` passes
        catchError((err: any) => { ... }),
        map(response => ({ result: response }))
      );
    }
    // Show error message
    return NEVER;               // never emit when `someCondition` fails
  }
)).subscribe({
  next: result => console.log(result),
  error: error => console.log(error)
});
person Michael D    schedule 15.11.2020
comment
Большое спасибо за помощь. Я проверю и сообщу вам о результате через несколько часов. Кстати, проголосовал за этот хороший подход;) Если есть необходимость, вы можете обновить свое решение в ближайшие часы, что я не смогу попробовать. - person Prag Dopravy; 15.11.2020
comment
Я попробовал этот подход и вижу, что мне нужно обработать ответ в первом блоке catchError(). И затем я возвращаю err в этом блоке, но не могу получить его в переменной switchMap как response. Итак, должен ли я получить значение, возвращаемое из catchError в блоке switchMap? - person Prag Dopravy; 15.11.2020
comment
Решение этого вопроса уже рассмотрено в пункте ответа 1. - person Michael D; 15.11.2020
comment
Могу ли я также применить некоторую логику в блоке catchError? Потому что я улавливаю только какой-то ответ в этом блоке, но я не уверен, что это правильное место для обработки такой логики. Есть идеи? - person Prag Dopravy; 17.11.2020
comment
Вы можете обрабатывать логику в блоке catchError. Но это зависит от логики. - person Michael D; 17.11.2020

Вы можете реализовать с помощью iif

this.demoService
   .create(...)
   .pipe(
     // tap first to be sure there's actually a response to process through
     tap(console.log),

     // You can use any condition in your iif, "response.user.exists" is just a sample
     // If the condition is true, it will run the run the update$ observable
     // If not, it will run the default$
     // NOTE: All of them must be an observable since you are inside the switchMap
     switchMap(response => 
      iif(() => 
        response.user.exists, 
        this.demoService.update(response.id),    // Pass ID 
        of('Default Random Message')
      )
     ),
     catchError((err: any) => { ... })
   );

person KShewengger    schedule 15.11.2020
comment
Большое спасибо за помощь. Я проверю и сообщу вам о результате через несколько часов. Кстати, проголосовал за этот хороший подход;) - person Prag Dopravy; 15.11.2020
comment
Большое спасибо за помощь. Я проверю и сообщу вам о результате через несколько часов. Кстати, проголосовал за этот хороший подход;) Если есть необходимость, вы можете обновить свое решение в ближайшие часы, что я не смогу попробовать. - person Prag Dopravy; 15.11.2020
comment
Я только что попытался использовать этот подход. 1. Мне нужно передать id значение в update$ наблюдаемое (this.demoService.update(...)). Как я могу пройти это? 2. Как я могу использовать этот iif, определив переменные update$ и default$ (например, this.demoService.update(...)) внутри этого блока iif? - person Prag Dopravy; 15.11.2020
comment
@PragDopravy Обновил мой ответ, вы все еще можете использовать необработанную функцию, не назначая ее в переменной, и вы также можете передавать данные оттуда. Надеюсь, поможет :) - person KShewengger; 16.11.2020
comment
Большое спасибо за ваши Лины помогает. На самом деле этот метод также является наблюдаемым методом, и я не уверен, должен ли я также снова определять эти методы с их каналом, картой, переключателем, блоком catchError? В этом случае я вижу, что это будут запутанные спагетти :) Итак, не могли бы вы указать тело метода и место определения this.demoService.update(response.id)? Извините, но я действительно новичок, и я тоже запутался в спагетти в последние дни с этой проблемой. - person Prag Dopravy; 16.11.2020