typescript 如何在订阅中返回 Observable

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/49436886/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 05:17:40  来源:igfitidea点击:

How to return Observable inside a subscription

angulartypescriptnativescriptangular2-observables

提问by AsyncTask

I want to call http request inside a observable which makes select operation from database. I made two services, DbServiceand BackendService.

我想在一个 observable 中调用 http 请求,它可以从数据库中进行选择操作。我做了两个服务,DbServiceBackendService

BackendService makes http post requests and returns response data. In my design BackendService should subscribe to DbService for getting url, after that make http post request then return response data.

BackendService 发出 http post 请求并返回响应数据。在我的设计中,BackendService 应该订阅 DbService 来获取 url,然后发出 http post 请求然后返回响应数据。

BackendService can take url from DbService and try to make http request but couldn't. response data is (Json format)

BackendService 可以从 DbService 获取 url 并尝试发出 http 请求,但不能。响应数据为(Json 格式)

{"_isScalar":false,"source":{"_isScalar":false},"operator":{}}

I don't understand what happening here. My services and AppComponent file is below.

我不明白这里发生了什么。我的服务和 AppComponent 文件如下。

There is BackendService

有后端服务

import { Injectable } from "@angular/core";
import { getString, setString } from "application-settings";
import { Headers, Http, Response, RequestOptions } from "@angular/http";
import { Observable } from "rxjs/Observable";
import 'rxjs/add/observable/of';
import "rxjs/add/operator/do";
import "rxjs/add/operator/map";
import "rxjs/add/observable/throw";
import "rxjs/add/operator/catch";
import { DbService } from "./db.service";

@Injectable()
export class BackendService {
    static BaseUrl= "http://blabla.com"

    constructor(public http: Http, private db: DbService) {
    }

        sendPost(key: string, requestObj: Object):Observable<any>{
        console.log("sendPost: ");
        return new Observable(obs=> {
            let obs1 = this.db.getActionUrl(key);
            obs1.subscribe(value => {
                let url = BackendService.BaseUrl + value;

                console.log("key: ", key);
                console.log("url: ", url);
                var h = BackendService.getHeaders();
                obs.next(this.http.post(
                    url,
                    JSON.stringify(requestObj),
                    { headers: h }
                ).map((res: Response) => res.json()));
                // .catch((error: any) => Observable.throw(error.json())));
                obs.complete();
            }
            , error => {
                console.error("send post error: "+ error);
                obs.error(error);
            }
        );
        });
    }

    static getHeaders() {
        let headers = new Headers();
        headers.append("Content-Type", "application/json");
        headers.append("SESSION-ID", this.sessionId);
        // headers.append("Authorization", BackendService.appUserHeader);
        return headers;
    }
}

There is DbService

有 Db​​Service

import { Injectable } from "@angular/core";
import { Observable } from "rxjs/Observable";
import 'rxjs/add/observable/of';
import "rxjs/add/operator/do";
import "rxjs/add/operator/map";
import "rxjs/add/observable/throw";
import "rxjs/add/operator/catch";
import 'rxjs/add/operator/toPromise';

var Sqlite = require("nativescript-sqlite");

@Injectable()
export class DbService {
    private tableActions = "actions";

    private columnActionName = "name";
    private columnActionUrl = "url";


    private database: any;
    constructor() {
        console.log("DbService Constructor");
        (new Sqlite("my_app.db")).then(db => {
            db.execSQL("CREATE TABLE IF NOT EXISTS " + this.tableActions + " (" + this.columnActionName + " TEXT PRIMARY KEY, " + this.columnActionUrl +" TEXT)").then(id => {
                this.database = db;
                console.log("DB SERVICE READY");
            }, error => {
                console.log("CREATE TABLE ERROR", error);
            });
        }, error => {
            console.log("OPEN DB ERROR", error);
        });
    }


    public getActionUrl(key: string):Observable<any>{
    return new Observable(observer => { 
        if (key === "getApiMap") {
            observer.next("/getApiMap");
            observer.complete();
            return;
        }
        console.log("getActionUrl :" + key);
        this.database.all("SELECT * FROM " + this.tableActions).then(
            rows => {
                console.log(rows);
                observer.next(rows[0][this.columnActionUrl]);
                observer.complete();
            }, error => {
                console.log("SELECT ERROR: getActionUrl: ", error);
                observer.error(error);
            })
    });
    }
}

And there is my AppComponent which makes http requests...

还有我的 AppComponent 发出 http 请求...

//some imports

export class AppComponent {
    public resp: Observable<ModelMainGetApiMapRes>;
    public constructor(private bs: BackendService, private db: DbService) {
let req = new ModelMainGetApiMapReq()
    bs.sendPost("getApiMap", req, false).subscribe(
        (res: ModelMainGetApiMapRes) => {
            console.log("getapimap response received!");
            console.log(JSON.stringify(res));
            console.log("apimap version:" + res.api_version);

        },
        err => {
             console.error("error!", err);
        }
    );
 }

//some functions
}

the console output of app.component is

app.component 的控制台输出是

CONSOLE LOG file:///app/shared/backend.service.js:61:20: sendPost:
CONSOLE LOG file:///app/shared/backend.service.js:66:28: key:  getApiMap
CONSOLE LOG file:///app/shared/backend.service.js:67:28: url:  http://blabla.com/getApiMap
CONSOLE LOG file:///app/app.component.js:55:36: getapimap response received!
CONSOLE LOG file:///app/app.component.js:56:36: {"_isScalar":false,"source":{"_isScalar":false},"operator":{}}
CONSOLE LOG file:///app/tns_modules/tns-core-modules/profiling/profiling.js:10:16: ANGULAR BOOTSTRAP DONE. 7805.849
CONSOLE ERROR file:///app/tns_modules/@angular/core/bundles/core.umd.js:1486:24: ERROR Error: Uncaught (in promise): TypeError: undefined is not an object (evaluating 'res.api_version')

回答by Fetrarij

With your actual code in BackendService.ts:

使用BackendService.ts 中的实际代码:

return new Observable(obs=> {
    let obs1 = this.db.getActionUrl(key);
    obs1.subscribe(value => {
        let url = BackendService.BaseUrl + value;

        console.log("key: ", key);
        console.log("url: ", url);
        var h = BackendService.getHeaders();
        obs.next(this.http.post(
            url,
            JSON.stringify(requestObj),
            { headers: h }
        ).map((res: Response) => res.json()));
         obs.complete();
        ...
    });
 });

you emitted the http observable

你发出了 http observable

this.http.post(
                    url,
                    JSON.stringify(requestObj),
                    { headers: h }
                ).map((res: Response) => res.json())

that's why you got: {"_isScalar":false,"source":{"_isScalar":false},"operator":{}}when subscribing on it, it's an observable.

这就是为什么你得到:{"_isScalar":false,"source":{"_isScalar":false},"operator":{}}当订阅它时,它是一个可观察的。

The simplest solution with your code,you can emit the data after subscribing the second observable, something like:

最简单的代码解决方案,您可以在订阅第二个 observable 后发出数据,例如:

return new Observable(obs=> {
    let obs1 = this.db.getActionUrl(key);
    obs1.subscribe(value => {
        let url = BackendService.BaseUrl + value;
        console.log("key: ", key);
        console.log("url: ", url);
        var h = BackendService.getHeaders();
        this.http.post(
            url,
            JSON.stringify(requestObj),
            { headers: h }
        ).map((res: Response) => res.json())
         .subscribe(data => obs.next(data));
    });
});

But the better solutionis by using switchMapoperator: (or any other xxxxMap operator)

但更好的解决方案是使用switchMap运算符:(或任何其他 xxxxMap 运算符)

import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/map';
...
sendPost(key: string, requestObj: Object):Observable<any>{
    return this.db.getActionUrl(key)
            .map( value => BackendService.BaseUrl + value)
            .switchMap(url => this.http.post(
                url,
                JSON.stringify(requestObj),
                { headers: h }
            )
            .map((res: Response) => res.json()))
}