RxJS使用第一个流的输出运行第二个流

时间:2017-02-28 10:35:34

标签: javascript angular rxjs observable

我有一项确定位置的服务,它被写为Observable

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

const GEOLOCATION_ERRORS = {
    'errors.location.unsupportedBrowser': 'Browser does not support location services',
    'errors.location.permissionDenied': 'You have rejected access to your location',
    'errors.location.positionUnavailable': 'Unable to determine your location',
    'errors.location.timeout': 'Service timeout has been reached'
};

@Injectable()
export class GeolocationService {
    public getLocation(opts): Observable<any> {
        return Observable.create(observer => {
            if (window.navigator && window.navigator.geolocation) {
                window.navigator.geolocation.getCurrentPosition(
                    (position) => {
                        observer.next(position);
                        observer.complete();
                    },
                    (error) => {
                        switch (error.code) {
                            case 1:
                                observer.error(GEOLOCATION_ERRORS['errors.location.permissionDenied']);
                                break;
                            case 2:
                                observer.error(GEOLOCATION_ERRORS['errors.location.positionUnavailable']);
                                break;
                            case 3:
                                observer.error(GEOLOCATION_ERRORS['errors.location.timeout']);
                                break;
                        }
                    }, opts);
            } else {
                observer.error(GEOLOCATION_ERRORS['errors.location.unsupportedBrowser']);
            }
        });
    }
}

export var GeolocationServiceInjectables: Array<any> = [
    { provide: GeolocationService, useClass: GeolocationService }
];

然后在我的HttpService中,我想用位置服务

的输出构造查询URL
import { Observable } from 'rxjs/Observable';
import { Injectable, Inject } from '@angular/core';
import { Http, Response } from '@angular/http';
import { GeolocationService } from './location.service';
import { WeatherItem } from '../weather-item/weather-item.model';

export const OpenWeatherMap_API_KEY: string = 'SOME_API_KEY';
export const OpenWeatherMap_API_URL: string = 'http://api.openweathermap.org/data/2.5/forecast';

@Injectable()
export class HttpService {
    constructor(private http: Http,
        private geolocation: GeolocationService,
        @Inject(OpenWeatherMap_API_KEY) private apiKey: string,
        @Inject(OpenWeatherMap_API_URL) private apiUrl: string) {
    }

    prepaireQuery(): void {
        this.geolocation.getLocation({ enableHighAccuracy: false, maximumAge: 3 }).subscribe(
            (position) => {
                let params: string = [
                    `lat=${position.latitude}`,
                    `lon=${position.longitude}`,
                    `APPID=${this.apiKey}`,
                ].join('&');
              //  return `${this.apiUrl}?${params}`;
            }
        );

    }

    getWeather(): Observable<WeatherItem[]> {
        return this.http.get(/*there should be the url*/)
            .map((response: Response) => {
                return (<any>response.json()).items.map(item => {
                    const city = {
                        city: item.city.name,
                        country: item.city.country,
                    }
                    return item.list.map(entity => {
                        return new WeatherItem({
                            temp: entity.main.temp,
                            temMin: entity.main.temp_min,
                            temMax: entity.main.temp_max,
                            weatherCond: entity.weather.main,
                            description: entity.weather.description,
                            windSpeed: entity.wind.speed,
                            icon: entity.weather.icon,
                            city,
                        })
                    })
                })
            })
    }
}
export var HttpServiceInjectables: Array<any> = [
    { provide: HttpService, useClass: HttpService },
    { provide: OpenWeatherMap_API_KEY, useValue: OpenWeatherMap_API_KEY },
    { provide: OpenWeatherMap_API_URL, useValue: OpenWeatherMap_API_KEY }
];

问题是如何在请求之前获取URL。我已经看到了取消订阅()的解决方案,但我认为它并不是那么好。我已经考虑过merge(),但我不确定它是我真正想要的。

2 个答案:

答案 0 :(得分:1)

您可能正在寻找RxJs的mergeMap运算符。

mergeMap做的是自动订阅源可观察对象,然后让你在你的内部可观察对象中处理它的结果,然后最终展平你的输出。

在此示例中,您调用firstUrl并使用您在第二次调用secondUrl时从该请求中获得的结果:

this.http.get(`{firstUrl}`)
   .mergeMap(res => this.http.get(`{secondUrl}/{res.json()}`))
   .subscribe(...)

我没有具体说明你的代码,因为我不确定你想要做什么。但我希望这会对你有所帮助!

答案 1 :(得分:1)

可以使用map / flatMap组合完成此操作:

getWeather(): Observable<WeatherItem[]> {
    return this.geolocation.getLocation({ enableHighAccuracy: false, maximumAge: 3 })
        .map((position) => {
            let params: string = [
                `lat=${position.latitude}`,
                `lon=${position.longitude}`,
                `APPID=${this.apiKey}`,
            ].join('&');
          return `${this.apiUrl}?${params}`;
        })
        .flatMap(url => this.http.get(url)
        .map((response: Response) => {
            return (<any>response.json()).items.map(item => {
                const city = {
                    city: item.city.name,
                    country: item.city.country,
                }
                return item.list.map(entity => {
                    return new WeatherItem({
                        temp: entity.main.temp,
                        temMin: entity.main.temp_min,
                        temMax: entity.main.temp_max,
                        weatherCond: entity.weather.main,
                        description: entity.weather.description,
                        windSpeed: entity.wind.speed,
                        icon: entity.weather.icon,
                        city,
                    })
                })
            })
        })
}