Observable Продолжайте вызывать API и изменять параметры в зависимости от условия

Я прочитал повторение Rx.js, чтобы узнать, как я могу продолжайте вызывать API на основе ответа, который я получаю от API. Я вызываю API, который может отправлять только 2k записей за раз. API вернет мне значение для отправки, чтобы я мог продолжать получать записи до тех пор, пока они не вернут готовое значение.

Итак, поток выглядит следующим образом:

  1. Сделайте GET запрос параметра запроса reqMode='':
  2. получить ответ с последним массивом, содержащим reqMode с value или done.
  3. Если я получаю value, мне нужно сделать тот же запрос, но отправить параметр reqMode со значением.
  4. Если я получу done, я остановлюсь и верну все записи с момента первого звонка.

Я получаю первый набор значений, когда subscribing normally, но это была бы моя попытка после прочтения документов, но это не имеет смысла:

getRecords(){
    let url = this.url + 'reqMode=';
    return this.http.get(url)
            .doWhile() //What would I do here
}

При попытке сделать .doWhile с Observable типа Observable<response>. Я ищу любую альтернативу, используя Observables для того, что мне нужно сделать.


person Abdullah Rasheed    schedule 30.09.2016    source источник
comment
angular2 поставляется с rxjs5-beta, и на основании документации вы не сможете использовать .doWhile... по крайней мере, пока: github.com/ReactiveX/rxjs/blob/master/. Вы ищете альтернативные решения? или только для rxjs4?   -  person mrcolombo    schedule 30.09.2016
comment
Меня устраивает альтернатива.   -  person Abdullah Rasheed    schedule 30.09.2016


Ответы (2)


Я не думаю, что repeat() является хорошим оператором для этого. Если я правильно вас понимаю, вы хотите повторить HTTP-запрос на основе ответа на предыдущий запрос. Оператор repeat() удобен, если вы хотите повторить один и тот же запрос несколько раз.

Я бы использовал concatMap() и рекурсивно вызывать себя до тех пор, пока reqMode не будет равно "done":

Посмотреть демонстрацию в реальном времени: http://plnkr.co/edit/w0DdepslTaKrLSB3aIkA

import {Observable, Subject} from 'rxjs';

const result = new Subject();
const closeBuffer = new Subject();
const buffer = result.buffer(closeBuffer.asObservable());

function sendHttpRequest(reqMode) {
  return Observable.of('{"reqMode":' + reqMode + '}')
    .map(response => JSON.parse(response))
    .concatMap(data => {
      console.log('HTTP Response:', data);
      // Add data to the buffer of results
      result.next(data);

      if (data.reqMode == 'done') {
        // Return an empty value wrapped as an Observable so concatMap can work
        // with it and emit onNext when it completes (which is immediately
        // thanks to the `.of()` operator).
        return Observable.of(null);
      } else {
        // Simulate that the next call returns 'done'
        return sendHttpRequest('"done"');

        // Uncomment this for real usage
        //return sendHttpRequest(data.reqMode);
      }
    });
}

// Subscribe to the buffer where I'll receive the value.
buffer.subscribe(val => console.log('Next: ', val));

// Simulate HTTP request with reqMode = 42
sendHttpRequest(42).subscribe(() => {
  console.log('done');
  // Emit values from the buffer.
  closeBuffer.next(null);
  closeBuffer.complete();
});

Я использую оператор of() для моделирования запрос и вернуть значение, обернутое как Observable. Я также использую Subject для хранения всех ответов, буферизованных с помощью buffer() оператор. Я подписываюсь на буфер, чтобы получить окончательный массив ответов (если вы завернете этот код в функцию, вы, скорее всего, вернете buffer, где вы сможете подписаться позже).

Ответ следующий:

HTTP Response: Object {reqMode: 42}
HTTP Response: Object {reqMode: "done"}
Next:  [Object, Object]

См. аналогичный вопрос: Angular 2 + rxjs — как вернуть поток объектов, полученных несколькими последующими HTTP-запросами

person martin    schedule 02.10.2016
comment
выглядит хорошо! recursion был моим слабым местом. Пришло время перейти к новому ... Я попробую это прямо сейчас - person Abdullah Rasheed; 02.10.2016
comment
@inspired Я обновил свой ответ, я не понимал, что вы хотите сложить все ответы до done, что означает, что их может быть несколько, я думаю (?). Теперь это немного сложнее, но я думаю, что это все еще понятно. - person martin; 02.10.2016
comment
о, я пробовал ваш метод и просто отправлял каждый ответ данных в массив, чтобы собрать их все, но я думаю, что тогда мой метод связывается с внешним массивом. - person Abdullah Rasheed; 02.10.2016
comment
@inspired Да, это тоже вариант. Я думаю, вам нужно иметь отдельную переменную, в которую вы помещаете свои результаты, потому что цепочка Observable работает с ответами HTTP. Более функциональным способом был бы вызов sendHttpRequest() вместе с накопленным массивом результатов (например, sendHttpRequest('"done"', [obj1, obj2])), но это сделало бы его довольно запутанным и даже не необходимым. - person martin; 02.10.2016
comment
Я еще не пробовал буфер, но объединение значений в массив работает отлично! - person Abdullah Rasheed; 02.10.2016

Поэтому я сделал пример того, как вы можете сделать это, используя обертку observer и используя .repeat().

Вся логика в app.component.ts

Просмотреть этот плункер

Я оставил комментарии в коде, но, по сути, он сделает http-запрос, увеличит счетчик, а затем сделает еще один запрос с другим номером запроса. Это будет повторяться, пока не достигнет 5.

Вам придется изменить его, чтобы «условие повторения» представляло вашу логику.

Надеюсь, это поможет!

person mrcolombo    schedule 30.09.2016
comment
@mrcolombo - Как бы вы добавили к этому интервал, чтобы повторять действие только каждую секунду? - person Donal Rafferty; 16.11.2016
comment
@DonalRafferty, добавив .delay(1000) перед .repeat(), заставит его выполняться каждую секунду. - person mrcolombo; 18.11.2016