限制rxjs进行的http调用

时间:2016-01-22 20:58:29

标签: javascript typescript rxjs

我正在编写一项服务,人们可以在Spotify播放列表中粘贴网址,然后在不同的服务中导出播放列表。对于每个跟踪请求中粘贴的URL,需要对Spotify api进行。

此代码:

        Rx.Observable.fromArray<ITrackIdentifier>( this._allTracks )
            .pluck<string>( "id" )
            .distinct()
            .flatMap(
                ( trackId ) => this.spotifyService.lookupTrack( trackId ).
                    catch( ( error ) => this.handleError( error ) ))
            .subscribe(
                ( result ) => this.handleTrackLookupResult( result ),
                ( error ) => this.handleError( error ),
                () => this.handleComplete()
            );
  1. 从ITrackIdentifiers列表中创建一个observable
  2. 获取轨道标识符的id以创建可观察的字符串(ID)
  3. 删除列表中的任何重复ID
  4. 为每个进行spotify(并捕获错误)的http调用创建一个observable
  5. 将所有这些可观察对象的结果合并为一个带有flatmap的流
  6. 除了添加大量曲目之外,这实际上工作正常。我的一个示例播放列表有超过500个曲目,因此立即进行500次调用,浏览器需要处理它们/从缓存中返回项目,因此浏览器速度很慢并且锁定并且当我超过api调用限制时,spotify会返回错误负载

    我希望能够只说10个同时运行的呼叫。 Merge with maxConcurrent设置似乎是Stackoverflow中讨论的完美解决方案。

    这看起来像这样:

            Rx.Observable.fromArray<ITrackIdentifier>( this._allTracks )
                .pluck<string>( "id" )
                .distinct()
                .map(
                    ( trackId ) => this.spotifyService.lookupTrack( trackId ).
                        catch( ( error ) => this.handleError( error ) ))
                .merge(10)
                .subscribe(
                    ( result ) => this.handleTrackLookupResult( result ),
                    ( error ) => this.handleError( error ),
                    () => this.handleComplete()
                );
    

    但它只是不起作用。在Chrome网络调试器中,您可以看到所有同时进行的呼叫,并且大多数时间排队等待它们失败。

    为什么这不起作用?我怎么能解决这个问题?

    以下是此阶段项目的Github checkin

2 个答案:

答案 0 :(得分:3)

使用spotifyService.lookupTrack的代码存在的问题是Observable不会返回Promise而是Observable。某些flatMap函数(如Observable)将handle Promises as well,但PromiseObservable之间的区别在于Promise是懒惰的,虽然console.log = x => {var d = document,b=d.body,p=d.createElement('pre'); p.style.margin = "0"; p.appendChild(d.createTextNode(''+x)); b.appendChild(p); window.scrollTo(0, b.scrollHeight); }; function log_delay(timeout, value) { return new Promise(resolve => { console.log('Start: ' + value); setTimeout(() => { console.log('End: ' + value); resolve(value); }, timeout); }); } Rx.Observable.range(0, 6) .map(x => Rx.Observable.defer( () => log_delay(1000, x) .catch(e => console.log('Inner catch')) )) .merge(2) .subscribe( s => console.log('Result: ' + s), s => console.log('Error: ' + s), s => console.log('Complete') );不是。用户3743222建议,您可以使用Observable.defer从承诺工厂函数中创建一个惰性可观察对象。这个小例子是用JavaScript而不是TypeScript,因此它可以在这里运行。

<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.0.7/rx.all.js"></script>
text-center

答案 1 :(得分:0)

我设法让它按照我想要的方式工作,但我仍然很好奇为什么合并不起作用。 这里构建了唯一ID的列表,然后我们使用concatMap为每个id创建一个Observable,然后等待延迟,然后再转到下一个项目:

        Rx.Observable.fromArray<ITrackIdentifier>( this._allTracks )
            .pluck<string>( "id" )
            .distinct()
            .concatMap( ( id, index ) => Rx.Observable.interval( 50 ).take( 1 ).map( () => { return id } ) )
            .flatMap(
                ( trackId ) => this.spotifyService.lookupTrack( trackId ).
                catch( ( error ) => this.handleError( error ) ))
            .subscribe(
                ( result ) => this.handleTrackLookupResult( result ),
                ( error ) => this.handleError( error ),
                () => this.handleComplete()
            );

在这个例子中,我在每次通话之间等待50ms。这大大减少了错误。

这是现阶段项目的Github checkin