Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angular/31.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Asynchronous 如何在Angular2循环/映射中执行异步HTTP请求并修改原始循环数组?_Asynchronous_Angular_Observable_Angular2 Http - Fatal编程技术网

Asynchronous 如何在Angular2循环/映射中执行异步HTTP请求并修改原始循环数组?

Asynchronous 如何在Angular2循环/映射中执行异步HTTP请求并修改原始循环数组?,asynchronous,angular,observable,angular2-http,Asynchronous,Angular,Observable,Angular2 Http,这里是Angular2新手,想知道如何在数组模式上执行此异步请求(不太确定模式名称) 假设我使用Angular2的Http获得一个YouTube播放列表,其中每个返回项中都包含videoId。然后我需要使用videoId循环浏览这个项目,并向YouTube发出另一个请求,以获取单独的视频信息。这可以是另一个常见的HTTP请求,获取列表,然后循环遍历列表,获取每个项目的详细信息 let url=[YouTube API V3列表url] 让我们来看看https://www.googleapis.c

这里是Angular2新手,想知道如何在数组模式上执行此异步请求(不太确定模式名称)

假设我使用Angular2的
Http
获得一个YouTube播放列表,其中每个返回项中都包含
videoId
。然后我需要使用
videoId
循环浏览这个项目,并向YouTube发出另一个请求,以获取单独的视频信息。这可以是另一个常见的HTTP请求,获取列表,然后循环遍历列表,获取每个项目的详细信息

let url=[YouTube API V3列表url]
让我们来看看https://www.googleapis.com/youtube/v3/videos?part=contentDetails,统计数字;
this.http.get(url)
.map(res=>res.json())
.订阅(数据=>{
让newArray=data.items.map(条目=>{
让videoUrl=detailUrl+'&id='+entry.id.videoId+
“&key=”+this.googleToken;
this.http.get(videoUrl)
.map(videoRes=>videoRes.json())
.订阅(视频数据=>{
控制台日志(视频数据);
//下面是如何将videoData连接到data.items中的每个项。
});
});
});
所以上面的代码确实有效。但我仍然有两个问题:

  • 如何将
    videoData
    连接回
    data.items
    ,并确保
    data.items
    中的正确项与正确的
    videoData
    连接(使用
    data.items中相关项的
    条目.id.videoId
    videoData
    )并创建新的
    newArray

  • 我如何知道什么时候一切都完成了,以及在所有异步请求完成后基于所有数据做些什么

  • 新数组保持第一个HTTP请求的相同顺序。当第一个HTTP使用order by viewCount访问YouTube搜索列表API时。像上面这样简单的嵌套观察将不会保持原始顺序

  • 更新


    使用inoabrian解决方案,我可以成功地实现上述三点。但是当我再次调用myvideoservice(比如将url更改为new NextPageToken)时,主题-this.\u videoObject有两个观察者。再次加载,它有3个观察者,以此类推。我需要一种方法来重置的主题,只有一个观察员,这样我就不会有重复的视频。这是清除/重置主题的最佳实践方式吗

    您已经可以在
    .map
    函数中访问
    条目
    ,您应该可以使用点符号将另一个值添加到该条目上。因此,请这样做:

    let url = [YouTube API V3 list url]
    let detailUrl = 'https://www.googleapis.com/youtube/v3/videos?part=contentDetails,statistics';
    
    this.http.get(url)
        .map(res => res.json())
        .subscribe(data => {
            let newArray = data.items.map(entry => {
    
                let videoUrl = detailUrl + '&id=' + entry.id.videoId + 
                                           '&key=' + this.googleToken;
    
                this.http.get(videoUrl)
                    .map(videoRes => videoRes.json())
                    .subscribe(videoData => {
                         console.log(videoData);
    
                         entry.videoData = videoData;
    
                         //Here how to I join videoData into each item inside of data.items. 
                    });
        });
    });
    
    JS排序-


    假设您只需要修改数据,一旦所有子请求完成

    这就是你需要的

    this.http.get(url)//原始请求
    .map(res=>res.json())//从响应中提取json
    .switchMap(data=>{//接管原始数据,并使subscriber等待sub请求完成
    让modifiedRequest=new Subject();//在所有子请求完成后,可观察到发布修改的数据
    让allSubRequests=data.items.map(条目=>{//子请求数组
    让videoUrl=detailUrl+'&id='+entry.id.videoId+
    “&key=”+this.googleToken;
    返回此.http.get(videoUrl)
    .map(videoRes=>videoRes.json())//为每个子请求提取json
    .map(videoJson=>{//使用videoJson中的数据修改原始的“data”对象(我不知道格式,所以无法告诉如何操作)
    //在此处修改您的“数据”
    return videoJson;//让流继续
    });
    });
    让mergedSubRequests=Observable.range(0,1).merge.apply(null,allSubRequests);//合并所有子请求以了解何时完成所有子请求
    subrequests.subscribe(//订阅合并的可观察对象
    (d) =>{},//来自每个子请求的数据,我们不需要它
    (e) =>{},//每个子请求都有错误,请根据需要处理它
    ()=>{//success,在所有子请求完成后调用
    modifiedRequest.next(数据);//发送修改后的数据
    modifiedRequest.complete();//完成临时可观察的
    }
    );
    return modifiedRequest;//提供带有修改数据的可观察对象,给原始请求的订阅者,他们将不知道差异
    });
    

    所需导入:

    从'rxjs/Observable'导入{Observable};
    从'rxjs/Subject'导入{Subject};
    导入'rxjs/add/operator/map';
    导入“rxjs/add/operator/merge”;
    导入“rxjs/add/observable/range”;
    导入'rxjs/add/operator/switchMap';
    
    希望它有帮助,但不是经过测试:)

    现在,
    source
    发出的每个项目都是播放列表中的一个条目,与
    videoData
    字段中的视频数据合并(您的第一个问题)

    source
    完成所有操作(您的第二个问题)


    通过设置
    maxConcurrentRequest
    ,可以控制对
    detailUrl
    的最大并发请求数。当
    maxConcurrentRequest
    =1时,
    source
    将以与播放列表中的项目相同的顺序发出项目。如果您不关心顺序,请增加
    maxConcurrentRequest
    以获得更快的速度

    这部分让我不知所措:让mergedSubRequests=Observable.range(0,1).merge.apply(null,allSubRequests);还有你对switchmap的使用。我们能解释一下为什么在这里使用switchmap吗?因为switchmap取消了之前的可观察性,对吗?我只是测试了你的代码,它对我不起作用。。。我在switchMap之后放了一个console.log(数据),没有控制台注销。我在r前面放了一个console.log(videoJson)
    // You need to set up a subject in your sevice
    private _videoObject = new Subject < any > ();
    videoDataAnnounced$ = this._videoObject;
    
    
    let url = [YouTube API V3 list url]
    let detailUrl = 'https://www.googleapis.com/youtube/v3/videos?part=contentDetails,statistics';
    
    this.http.get(url)
        .map(res => res.json())
        .subscribe(data => {
            this.cache = data.items;
            data.items.forEach(entry => {
                let videoUrl = detailUrl + '&id=' + entry.id.videoId + '&key=' + this.googleToken;
                this.http.get(videoUrl)
                    .map(videoRes => videoRes.json())
                    .subscribe(videoData => {
                        // This will announce that the full videoData is loaded to what ever component is listening.
                        this._videoObject.next(videoData);
                    });
            });
        });
    
    
    
    // In your constructor for your component you can subscribe to wait on the data being loaded.
    public newVideos: any[] = [];
    constructor(private myServiceToLoadVideos: myvideoservice) {
        let self = this;
        this.myServiceToLoadVideos.videoDataAnnounced$.subscribe((video) => {
            // Here you could loop through that
            self.myServiceToLoadVideos.cache.map(d => {
                if (d.id == video.id) {
                    // Here you have the old data set without the details which is (d) from the cached data in the service.
                    // And now you can create a new object with both sets of data with the video object
                    d.snippet = video.items[0].snippet;
                    d.statistics = video.items[0].statistics;
                    d.contentDetails = video.items[0].contentDetails;
                    this.posts = this.posts.concat(d);
                }
            });
            if(this.posts.length == this.cache.length){
              this.posts.sort(function(l,r){
                // if l is < r then it will return a negative number
                // else it will return a positive number.
                // That is how this sort function works
                return l.viewCount - r.viewCount;
              });
    
              // Here you will have all of the data and it will be sorted in order.
            }
        });
    }
    
    The Subject is an observable - http: //reactivex.io/documentation/subject.html
    
        When you listen to the Subject it is not like q.all it will complete one by one until they complete.
    
    The cache in the service call is just to keep track of the playlist.
    Then when you receive the details in the constructor you can match them back up and keep order with the cache.
    
    Q: "And when subscribe to the subject as in your code, the result is when ALL the "
    next " in _videoObject finished, right?"
    A: No, when each individual finishes you will get once at a time.
    
    let addDetail = (entry) => {
      let videoUrl = detailUrl + '&id=' + entry.id.videoId + 
                    '&key=' + this.googleToken;
    
      return this.http.get(videoUrl)
        .map(videoRes => Object.assign({videoData: videoRes.json()}, entry)
    };
    
    let maxConcurrentRequest = 1;
    let source = this.http.get(url)
      // any of mergeMap, concatMap, switchMap... will do
      .mergeMap(res => Observable.from(res.json().items))
      .mergeMap(addDetail, null, maxConcurrentRequest);
    source.subscribe( /* Do anything you wish */ );