Angular2:动态同步http请求

时间:2016-04-05 16:40:52

标签: javascript http angular rxjs observable

目标:制作一系列同步http请求,并能够将它们订阅为一个可观察的流。

示例(不工作):

let query_arr = ['test1','test2','test3']

function make_request(query_arr){

    if (query_arr.length){

        let payload = JSON.stringify(query_arr[0]);
        let headers = new Headers();

        query_arr.splice(0,1);

        this.http.post('https://endpoint/post',payload,{headers:headers})
            .map((res:Response) => {make_request(query_arr)})

    }

}.subscribe(
    data => console.log('finished http request, moving on to next http request'),
    err => console.error(err),
    () => console.log('all http requests have been finished')
);

make_request(query_arr)

目标功能:

  • 需要知道每个回复何时返回
  • 必须知道所有回复何时返回

3 个答案:

答案 0 :(得分:5)

您需要利用flatMap运算符来串行执行请求(一个接一个)。为此,您需要递归地构建数据处理链。这里的要点是在前一个observable(前一个请求返回的那个)上调用操作符。

这样,请求将在执行之前等待前一个请求完成。订阅时提供的回调将在执行所有请求时被调用。

以下是此方法的示例实现:

makeRequest(queryArr, previousObservable){
  if (queryArr.length) {
    let payload = JSON.stringify(queryArr[0]);
    let headers = new Headers();
    (...)

    queryArr.splice(0,1);

    var observable = null;
    if (previousObservable) {
      observable = previousObservable.flatMap(() => {
        return this.http.post('https://testsoapi.apispark.net/v1/entities', payload,{
            headers:headers
          })
          .map((res:Response) => res.json())
          .do(() => {
            console.log('request finished');
          });
      });
    } else {
      observable = this.http.post('https://testsoapi.apispark.net/v1/entities', payload, {
        headers:headers
      })
        .map((res:Response) => res.json())
        .do(() => {
          console.log('request finished');
        });
    }

    return this.makeRequest(queryArr, observable);
  } else {
    return previousObservable;
  }
}

这个方法最初可以这样调用:

test() {
  let queryArr = [
    { val: 'test1' },
    { val: 'test2' },
    { val: 'test3' }
  ];

  this.makeRequest(queryArr).subscribe(
    () => {
      console.log('all requests finished');
    });
}

请参阅此plunkr:https://plnkr.co/edit/adtWwckvhwXJgPDgCurQ?p=preview

答案 1 :(得分:1)

您的代码中也存在一些语法错误,需要解决。但是,除了您之外,您可以使用concatMap + defer来大大简化。

let query_arr = ['test1','test2','test3'];
let self = this;

Rx.Observable.from(query_arr).map(JSON.stringify)
  .concatMap(payload => {
    let headers = new Headers();
    return Rx.Observable.defer(() => {
      self.http.post('https://endpoint/post',payload,{headers:headers});
    });
  }, resp => resp.json())
  .subscribe(
    data => console.log('finished http request, moving on to next http request'),
    err => console.error(err),
    () => console.log('all http requests have been finished')
  );

这个的基本思想是它会将查询数组转换为Observable,然后它会急切地创建一系列只在订阅时才会执行的延迟请求。但是,通过将帖子包装在defer中,每个请求只会在前一个请求完成时发送。

答案 2 :(得分:0)

或者在typescript中的非递归版本,您将数组提供给forkjoin

在return observableObj(res.json())中你知道从httpcall返回的每个响应

在订阅中,您知道何时返回所有响应以及值数组

const observableObj = (obj) => Observable.of(obj)

class Requests {

 private query_arr = ['test1','test2','test3']
 private url = 'https://testsoapi.apispark.net/v1/entities'

 public make() {
   this.processHttp().subscribe(
        (d) => {
          console.log(d)              
        },
        (e) => {
          console.log(e)
        },
        () => {
          console.log("http calls are done")
        })

 }

 private httpCall(options : RequestOptions) : Observable<Response> {
   let username : string = 'xxx'
   let password : string = 'yyy'
   let headers = new Headers()
   headers.append("Authorization", "Basic " + btoa(username + ":" + password))
   headers.append("Content-Type", "application/x-www-form-urlencoded")
   options.headers = headers
   return this.http.get(this.url,options)
 }

 private createRequestOptions(option1 : string) {
   let data = {'option1':option1}
   let params = new URLSearchParams()
   for(var key in data) {
     params.set(key, data[key])
   }
   let options = new RequestOptions({
     search: params
   })
   return options
 }

 private processHttp() {
    return Observable.forkJoin(
        this.query_arr.map(option => {
            return this.httpCall(createRequestOption(option)).flatMap((res: Response) => {
                return observableObj(res.json())
            })
        }))            
 }
}