How to convert a sequence of Promises into Rx.Observable with RxJS?

只愿长相守 提交于 2019-12-11 05:47:36

问题


I'm trying to create an Rx.Observable from a chain of Promises with RxJS. The difference from this question is that I have unknown number of Promises, and every Promise depends on the result of the previous one.

Basically I have a sequence of pages, connected with "next page" links.

What I want the function to do is:

  • Wait for Promise<>
  • Provide the result (fire observer.onNext())
  • Check if there is a next page link
  • Create next Promise<> with that link
  • Repeat until there are pages remained

I tried the following:

private getPages<T>(firstPromise: PromiseLike<IODataCollectionResult<T>>): Rx.Observable<T> {

    let observable = Rx.Observable.create<T>(async obs => {
        let page = await firstPromise;
        page.value.forEach(v => obs.onNext(v));

        while (page['@odata.nextLink']) {
            let nextPageUrl = <string>page['@odata.nextLink'];
            let nextPagePromise = <PromiseLike<IODataCollectionResult<T>>>this.resource(nextPageUrl).get().$promise;
            page = await nextPagePromise;
            page.value.forEach(v => obs.onNext(v));
        }

        obs.onCompleted();
    });

    return observable;
}

(IODataCollectionResult is a OData result, where '@odata.nextLink' is the next page url and .value is an array of values)

The problem is I can't compile that with TypeScript, it gives me an error:

Argument of type '(obs: Observer) => Promise' is not assignable to parameter of type '(observer: Observer) => void | Function | IDisposable'.

Which makes sense, because async function returns a Promise<void>, not a void.

Does it mean I cannot use async/await with the Rx.Observable.create()? How can I chain a sequence of Promises into an Observable?


回答1:


You can wrap the async function in something that voids its results:

function toVoid<A>(fn: A => Any): A => Void {
    return x => void fn(x)
}

(forgive my lacking knowledge of TypeScript, but I hope you can guess what it's supposed to do)

With that, you should be able to call

let observable = Rx.Observable.create<T>(toVoid(async obs => {
    …
}));

But maybe you shouldn't do that. Don't throw away the promise, use it instead to attach the appropriate error handler:

let observable = Rx.Observable.create<T>(obs => {
    (async () => {
        …
    }()).catch(err => {
        obs.onError(err);
    });
});



回答2:


The problem was solved using .then() + recursion, without async/await:

private getPages<T>(initialPromise: PromiseLike<IODataCollectionResult<T>>): Rx.Observable<T> {
    return Rx.Observable.create<T>(obs => {
        const getPage = (promise: PromiseLike<IODataCollectionResult<T>>) => {
            promise.then(page => {
                page.value.forEach(v => obs.onNext(v));
                if (page['@odata.nextLink']) {
                    let nextPageUrl = <string>page['@odata.nextLink'];
                    let nextPagePromise = <PromiseLike<IODataCollectionResult<T>>>this.resource(nextPageUrl).get().$promise;
                    getPage(nextPagePromise);
                }
                else {
                    obs.onCompleted();
                }
            });
        }
        getPage(initialPromise);
    });
}


来源:https://stackoverflow.com/questions/40238596/how-to-convert-a-sequence-of-promises-into-rx-observable-with-rxjs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!