繁体   English   中英

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

[英]Angular2/RXJS - Handling Potentially Long Queries

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

问题:如果用户选择少于几百个文件,则此方法很有效,否则会导致响应时间过长。 我不想限制用户可以选择的文件数量,我不想担心前端的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. 让端点立即返回一般响应,例如“将文件匹配上传并保存到您的配置文件”。 由于所有匹配的文件都持久保存在后端的数据库中,这样就可以了,然后我可以让前端每隔一段时间查询一次数据库以获取当前的匹配文件列表。 这看起来很丑陋,但我想我会把它扔出去。

欢迎任何其他解决方案。 能够以优雅的方式使用angular2 / observables来处理这种类型的持久查询,这将是一个很好的做法。

我建议您将搜索到的文件数量分解为可管理的批次,然后在返回结果时处理更多,即解决方案#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如果你想他们都并行执行,但似乎该服务器在这种情况下,资源的限制,如果我没有弄错。

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

另见 - https://www.npmjs.com/package/angular2-websocket - http://mmrath.com/post/websockets-with-angular2-and-spring-boot/ - http://www.html5rocks。 COM /德/教程/的WebSockets /基础/

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

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

getAll(urls:any[]):Observable {
  let observables = [];
  for(var i = 0; i < items.length; i++) {
    observables.push(this.http.get(urls[i]));
  }
  return Observable.forkJoin(observables);
}

someMethod(server:string) {
  let urls = [
   '${server}/fileService?somedata=a',
   '${server}/fileService?somedata=b',
   '${server}/fileService?somedata=c'];

  this.getAll(urls).subscribe(
      (value) => processValue(val),
      (err) => processError(err),
      () => onDone());
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM