Angular2 / RXJS - 处理潜在的长查询

时间:2016-03-29 21:22:57

标签: javascript api http angular rxjs

目标:应用程序的前端允许用户从本地计算机中选择文件,并将文件名发送到服务器。然后,服务器将这些文件名与位于服务器上的文件进行匹配。然后,服务器将返回所有匹配文件的列表。

问题:如果用户选择少于几百个文件,则效果很好,否则会导致响应时间过长。我不想限制用户可以选择的文件数量,我不想担心前端的http请求超时。

到目前为止的示例代码:

//html on front-end to collect file information
<div>
    <input (change)="add_files($event)" type="file" multiple>
</div>

//function called from the front-end, which then calls the profile_service add_files function
//it passes along the $event object
add_files($event){

    this.profile_service.add_files($event).subscribe(
        data => console.log('request returned'),
        err => console.error(err),
        () => //update view function
    );       
}

//The following two functions are in my profile_service which is dependency injected into my componenet
//formats the event object for the eventual query
add_files(event_obj){

        let file_arr = [];
        let file_obj = event_obj.target.files;

        for(let key in file_obj){
            if (file_obj.hasOwnProperty(key)){
                file_arr.push(file_obj[key]['name'])
            }
        }

        let query_obj = {files:title_arr};

        return this.save_files(query_obj)
}

//here is where the actual request to the back-end is made
save_files(query_obj){

    let payload = JSON.stringify(query_obj);
    let headers = new Headers();

    headers.append('Content-Type', 'application/json');
    return this.http.post('https://some_url/api/1.0/collection',payload,{headers:headers})
        .map((res:Response) => res.json())
}

可能的解决方案:

  1. 批量处理请求。重新编写代码,以便一次只调用25个文件调用配置文件服务,并在每个响应调用配置文件时再次使用接下来的25个文件调用。如果这是最好的解决方案,那么有一种优雅的方法可以用observable做到这一点吗?如果没有,我将使用递归回调,这应该工作正常。

  2. 让端点立即返回一般响应,例如“将文件匹配上传并保存到您的个人资料”。由于所有匹配的文件都持久保存在后端的数据库中,这样就可以了,然后我可以让前端每隔一段时间查询一次数据库以获取当前的匹配文件列表。这看起来很难看,但我想把它扔到那里。

  3. 欢迎任何其他解决方案。能够以优雅的方式使用angular2 / observables来处理这种类型的持久查询会很棒。

2 个答案:

答案 0 :(得分:3)

我建议您将搜索到的文件数量分解为可管理的批次,然后在返回结果时处理更多,即解决方案#1。以下是未经测试但我认为相当优雅的方法:

add_files(event_obj){

    let file_arr = [];
    let file_obj = event_obj.target.files;

    for(let key in file_obj){
        if (file_obj.hasOwnProperty(key)){
            file_arr.push(file_obj[key]['name'])
        }
    }

    let self = this;
    let bufferedFiles = Observable.from(file_arr)
        .bufferCount(25); //Nice round number that you could play with

    return bufferedFiles

       //concatMap will make sure that each of your requests are not executed
       //until the previous completes. Then all the data is merged into a single output
       .concatMap((arr) => {

         let payload = JSON.stringify({files: arr});
         let headers = new Headers();
         hearders.append('Content-Type', 'application/json');

         //Use defer to make sure because http.post is eager
         //this makes it only execute after subscription
         return Observable.defer(() => 
            self.post('https://some_url/api/1.0/collection',payload, {headers:headers})
       }, resp => resp.json());
}

concatMap将阻止您的服务器执行超过缓冲区大小的任何操作,方法是阻止新请求,直到返回上一个请求。如果你想让它们全部并行执行,你也可以使用mergeMap,但如果我没有弄错的话,服务器似乎是资源限制。

答案 1 :(得分:0)

我建议使用websocket连接,因为它们不会超时。

另见 - https://www.npmjs.com/package/angular2-websocket - http://mmrath.com/post/websockets-with-angular2-and-spring-boot/ - http://www.html5rocks.com/de/tutorials/websockets/basics/

另一种方法是轮询,其中客户端在定义的时间间隔内重复请求以从服务器获取当前处理状态。

发送多个请求并等待所有请求完成

foo
1
2
three
4