Как сделать HTTP-запрос с интервалом?

Я совершенно новичок в angular и rxjs. Я пытаюсь создать приложение angular2, которое получает некоторые данные из статически обслуживаемого текстового файла (локально на сервере), которые я хотел бы получить и сопоставить с моделью данных, используя http-провайдера Angular2 и карту rxjs в фиксированное время interval(5000). Чтобы отразить любые изменения в обслуживаемом текстовом файле.

Я знаю, что с rxjs 4.x вы можете использовать Observable.interval(5000) для выполнения задания, но, похоже, его нет в rxjs 5. Мой обходной путь в настоящее время обновляет все приложение, используя <meta http-equiv="refresh" content="5" >, который перезагружает всю страницу и, таким образом, перезагружает данные.

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

Любая помощь или другой/лучший способ будут очень признательны.

Что у меня есть до сих пор:

@Injectable()
export class DataService {

    constructor(private http:Http){}

    getData(url) {
        return this.http.get(url)
            .map(res => {
                return res.text();
            })
            .map(res => {
                return res.split("\n");
            })
            .map(res => {
                var dataModels: DataModel[] = [];
                res.forEach(str => {
                    var s = str.split(",");
                    if(s[0] !== "") {
                        dataModels.push(new DataModel(s[0], parseInt(s[1]), parseInt(s[2])));
                    }
                });
                return dataModels;
            })
    }
}

@Component({
selector: 'my-app',
template: `Some html to display the data`,
providers: [DataService],
export class AppComponent {

data:DataModel[];

constructor(dataService:DataService) {}

ngOnInit() {
    this.dataService.getData('url').subscribe(
        res => {
            this.data= res;

        },
        err => console.log(err),
        () => console.log("Data received")
        );
    }
}

Зависимости: package.json

"dependencies": {
  "angular2": "^2.0.0-beta.3",
  "bootstrap": "^4.0.0-alpha.2",
  "es6-promise": "^3.0.2",
  "es6-shim": "^0.33.13",
  "jquery": "^2.2.0",
  "reflect-metadata": "^0.1.2",
  "rxjs": "^5.0.0-beta.0",
  "systemjs": "^0.19.20",
  "zone.js": "^0.5.11"
},
"devDependencies": {
  "typescript": "^1.7.5"
}

index.html импортирует:

<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/systemjs/dist/system-polyfills.js"></script>

<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<script src="node_modules/angular2/bundles/router.dev.js"></script>
<script src="node_modules/angular2/bundles/http.dev.js"></script>

person Tony Krøger    schedule 10.02.2016    source источник


Ответы (6)


Как упоминали @Adam и @Ploppy, Observable.interval() теперь deprecated не является предпочтительным способом создания такого наблюдаемого. Предпочтительный способ сделать это через IntervalObservable или TimerObservable. [в настоящее время в Typscript 2.5.2, rxjs 5.4.3, Angular 4.0.0]

Я хотел добавить к этому ответу некоторое использование, чтобы продемонстрировать, что я нашел лучший способ сделать это в среде Angular 2.

Сначала ваша служба (созданная в angular cli с помощью команды «ng g service MyExample»). Предполагая, что служба RESTful (запрос http get возвращает json):

мой пример.service.ts

import { Injectable } from '@angular/core';
import { Http, Response} from "@angular/http";
import { MyDataModel } from "./my-data-model";
import { Observable } from "rxjs";
import 'rxjs/Rx';

@Injectable()
export class MyExampleService {
  private url = 'http://localhost:3000'; // full uri of the service to consume here

  constructor(private http: Http) { }

  get(): Observable<MyDataModel>{
    return this.http
      .get(this.url)
      .map((res: Response) => res.json());
  }
}

*** см. нижние обновления сервиса для Angular 5 ***

Теперь код вашего компонента ('ng g component MyExample'):

мой-example.component.ts:

import { Component, OnDestroy, OnInit } from '@angular/core';
import { MyDataModel } from "../my-data-model";
import { MyExampleService } from "../my-example.service";
import { Observable } from "rxjs";
import { IntervalObservable } from "rxjs/observable/IntervalObservable";
import 'rxjs/add/operator/takeWhile';

@Component({
  selector: 'app-my-example',
  templateUrl: './my-example.component.html',
  styleUrls: ['./my-example.component.css']
})
export class MyExampleComponent implements OnInit, OnDestroy {
  private data: MyDataModel;
  private display: boolean; // whether to display info in the component
                            // use *ngIf="display" in your html to take
                            // advantage of this

  private alive: boolean; // used to unsubscribe from the IntervalObservable
                          // when OnDestroy is called.

  constructor(private myExampleService: MyExampleService) {
    this.display = false;
    this.alive = true;
  }

  ngOnInit() {
    // get our data immediately when the component inits
    this.myExampleService.get()
      .first() // only gets fired once
      .subscribe((data) => {
        this.data = data;
        this.display = true;
      });

    // get our data every subsequent 10 seconds
    IntervalObservable.create(10000)
      .takeWhile(() => this.alive) // only fires when component is alive
      .subscribe(() => {
        this.myExampleService.get()
          .subscribe(data => {
            this.data = data;
          });
      });
  }

  ngOnDestroy(){
    this.alive = false; // switches your IntervalObservable off
  }
}

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

Обновлен код компонента ts для объединения подписок через TimerObservable:

import { Component, OnDestroy, OnInit } from '@angular/core';
import { MyDataModel } from "../my-data-model";
import { MyExampleService } from "../my-example.service";
import { Observable } from "rxjs";
import { TimerObservable } from "rxjs/observable/TimerObservable";
import 'rxjs/add/operator/takeWhile';

@Component({
  selector: 'app-my-example',
  templateUrl: './my-example.component.html',
  styleUrls: ['./my-example.component.css']
})
export class MyExampleComponent implements OnInit, OnDestroy {
  private data: MyDataModel;
  private display: boolean; // whether to display info in the component
                            // use *ngIf="display" in your html to take
                            // advantage of this

  private alive: boolean; // used to unsubscribe from the TimerObservable
                          // when OnDestroy is called.
  private interval: number;

  constructor(private myExampleService: MyExampleService) {
    this.display = false;
    this.alive = true;
    this.interval = 10000;
  }

  ngOnInit() {
    TimerObservable.create(0, this.interval)
      .takeWhile(() => this.alive)
      .subscribe(() => {
        this.myExampleService.get()
          .subscribe((data) => {
            this.data = data;
            if(!this.display){
              this.display = true;
            }
          });
      });
  }

  ngOnDestroy(){
    this.alive = false; // switches your TimerObservable off
  }
}

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

my-example-service.ts (с использованием HttpClient а-ля Angular 5):

import { Injectable } from '@angular/core';
import { HttpClient} from "@angular/common/http";
import { MyDataModel } from "./my-data-model";
import { Observable } from "rxjs";
import 'rxjs/Rx';

@Injectable()
export class MyExampleService {
  private url = 'http://localhost:3000'; // full uri of the service to consume here

  constructor(private http: HttpClient) { }

  get(): Observable<MyDataModel>{
    return this.http
      .get<MyDataModel>(this.url);
  }
}

Обратите внимание на изменение использования HttpClient вместо Http (устарело в angular5) и метода get, который позволяет анализировать ответ в нашей модели данных без использования оператора rxjs .map(). В то время как сервис меняется для angular 5, код компонента остается неизменным.

person ZackDeRose    schedule 07.06.2017
comment
Мне немного любопытно, почему вы используете живое логическое значение вместо того, чтобы отписываться от наблюдаемого в ngOnDestroy()? - person Tony Krøger; 04.07.2017
comment
Я следовал советам по ссылке и ссылка. Стоит прочитать, если интересно. Мой вывод заключался в том, что оператор takeWhile() предпочтительнее отмены подписки, поскольку он завершит Observable. - person ZackDeRose; 13.07.2017
comment
Благодарю вас! Очень понравился шаблон takeWhile. Не знал, что отписался от наблюдаемого. И гораздо предпочтительнее делать это, чем собирать подписки! - person Tony Krøger; 25.07.2017
comment
Спасибо, этот подробный ответ сэкономил мне много времени! - person Soumya Kanti; 25.07.2017
comment
Я получаю таймер свойства, который не существует в Observable .. знаете, почему? - person obey; 31.08.2017
comment
@ZackDeRose спасибо за ваш ответ, как правильно проверить этот код? Я задал вопрос здесь stackoverflow.com/questions/46546628/ - person Francesco Borzi; 03.10.2017
comment
Я в замешательстве. В исходном ответе говорится, что интервал устарел, но затем в обновленном коде используется таймер. Если интервал устарел, не будет ли и таймер устарел, поскольку они оба делают одно и то же? - person Curtis; 17.10.2017
comment
@obey - Скорее всего, это как-то связано с вашей версией rxjs. Ваша версия должна отображаться в файле package.json. Проверьте мое последнее обновление ответа, чтобы избежать использования свойства Observable.timer. - person ZackDeRose; 27.10.2017
comment
@Curtis устарел, вероятно, здесь неправильное слово. Вместо этого я бы сказал, что IntervalObservable.create() предпочтительнее при использовании машинописного текста, поскольку он использует преимущества наследования, которое предлагает TypeScript. Я не знаю, есть ли большая разница в функциях. См. мое недавнее обновление для решения, которое использует подкласс TimerObservable вместо свойства Observable.timer. - person ZackDeRose; 27.10.2017
comment
кажется, это будет генерировать новую подписку каждый раз, когда срабатывает таймер - person godblessstrawberry; 07.05.2019
comment
Кажется, что это постоянно меняющееся пространство. Сначала появился Observable.interval, затем (как отражено в этом ответе) появился IntervalObservable, а затем в RxJS 6 старые наблюдаемые классы были удалены и заменены такими функциями, как interval и timer. - person George Hawkins; 25.03.2020

Вы можете использовать метод interval Observable в Angular2.

import {Component,Input} from 'angular2/core';
import {Observable} from 'rxjs/Rx';

@Component({
  selector: 'my-app',
  template: `
    <div>
      {{message}}
    </div>
  `
})
export class AppComponent {
  constructor() {
    Observable.interval(500)
          .take(10).map((x) => x+1)
          .subscribe((x) => {
            this.message = x;
          }):
  }
}

Вот соответствующий plunkr, описывающий это: .

Исходя из этого, вы можете подключить свой HTTP-запрос:

initializePolling() {
  return Observable
     .interval(60000)
     .flatMap(() => {
       return this.dataService.getData('url'));
     });
}
person Thierry Templier    schedule 10.02.2016
comment
Спасибо за ваш быстрый ответ, и пример plunker был действительно полезным. И я заставил его работать локально над новым проектом с тем же импортом cdn, что и у вас. Но в моем основном проекте это все еще терпит неудачу: TypeError: Observable_1.Observable.interval не является функцией, поэтому я пытаюсь изменить свой импорт, чтобы он соответствовал вашему. Например, в npm нет rxjs-beta3. - person Tony Krøger; 10.02.2016
comment
Фактически [email protected] требует [email protected]. Я сделал тест с этими версиями, и это работает на моей стороне... - person Thierry Templier; 10.02.2016
comment
Какие версии вы используете? - person Thierry Templier; 10.02.2016
comment
Нашел ошибку: автоматический импорт с помощью Webstorm не удался. вместо импорта {Observable} из rxjs/Rx; что правильно, он добавил импорт с импортом {Observable} из rxjs/Observable; - person Tony Krøger; 10.02.2016
comment
И еще раз спасибо, часть initializePolling действительно очень помогла :) - person Tony Krøger; 10.02.2016
comment
Есть ли способ запустить интервал в первый раз, прежде чем ждать время? - person Tony Krøger; 10.02.2016
comment
Вы можете попробовать оператор startWith: Observable.interval(1000).startWith(0)... - person Thierry Templier; 10.02.2016
comment
Я думаю, что эта ссылка может вам помочь: gist.github.com/staltz/868e7e9bc2a7b8c1f754. В том числе в рубрике Кнопка обновления. - person Thierry Templier; 10.02.2016

Я думаю, что этот ответ больше недействителен из-за недавних изменений rxjs/observable. Теперь вам нужно использовать IntervalObservable.

https://github.com/ReactiveX/rxjs/blob/master/src/observable/IntervalObservable.ts

import { IntervalObservable } from 'rxjs/observable/IntervalObservable';

@Component({
  ...
})
export class AppComponent {
  n: number = 0;
  constructor() {
    IntervalObservable.create(1000).subscribe(n => this.n = n);
  }
}
person Ploppy    schedule 27.10.2016
comment
Как вы подписываетесь на наблюдаемое, возвращаемое службой с этим кодом? - person Jérémy JOKE; 24.05.2017
comment
Я не автор, но я просто подписываюсь на свой код с помощью этого IntervalObservable, подписываюсь на что-то, связанное с этим: IntervalObservable.create(10000).subscribe(response => { this.notificationService.getNotification().subscribe(notification => { this.notificationService.notification = notification; }); }); - person Denis Savenko; 26.03.2018

Для TypeScript (1.8.10 на момент ответа)/angular2 (rc1 на момент ответа) с [email protected] (beta.6 на момент ответа) вам нужно использовать IntervalObservable, который расширяет класс Observable

import {IntervalObservable} from 'rxjs/observable/IntervalObservable'

IntervalObservable.create(5000).take(10).map((x) => x + 1)
person Adam    schedule 05.05.2016
comment
rxjs/observable/IntervalObservable Строчное письмо. Можете ли вы привести пример того, как использовать это для опроса сервера в службах Angular2? - person Sulejman Sarajlija; 01.06.2016
comment
@sulejman Я полагаю, вы это выяснили, но все же: IntervalObservable.create(1000).subscribe(() =› { this.service.get().subscribe(x =› this.x = x) }); // это поможет - person yusijs; 07.11.2016
comment
извините, но как именно мы будем использовать это с нашим HTTP Observable? - person Motassem MK; 02.02.2017

это можно легко сделать через switchMap

Observable.timer(0, 5000)
          .switchMap((t) =>
            this.http.get(...).pipe(
                catchError(...)
            )
          )
          .subscribe(...)
person godblessstrawberry    schedule 08.05.2019
comment
Это то, за чем я пришел. Не могу поверить в правильный ответ. - person Machado; 23.07.2019

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

Это легко сделать с помощью switchMap:

Observable.timer(0, 5000)
  .switchMap(() => this.http.get(...).pipe(...)
  .subscribe(...)

Или в синтаксисе RxJS 6:

import { timer } from 'rxjs';
import { switchMap } from 'rxjs/operators';

timer(0, 5000) // repeats every 5 seconds
  .pipe(switchMap(() => this.http.get(...).pipe(...))
  .subscribe(...);

Вы даже можете использовать interval вместо timer:

import { interval } from 'rxjs';
import { switchMap } from 'rxjs/operators';

interval(5000) // repeats every 5 seconds
  .pipe(switchMap(() => this.http.get(...).pipe(...))
  .subscribe(...);
person Machado    schedule 23.07.2019