Angular 2 Long Polling

时间:2018-02-22 16:41:20

标签: angular rxjs long-integer polling

我正在尝试基于特定的REST接口实现长轮询请求。

请求:

GET url/?hash=9014532 HTTP/1.1

回应:

{
....
    "hash": 88144687,
    "value": 0
}

这样,只有在请求后值发生变化时,服务器才会响应。

此类请求需要将前一个响应中的最后一个已知哈希值作为查询字符串。 服务器延迟响应,直到哈希码不再等于REST客户端提供的哈希码,或者长轮询请求的配置超时已过期(默认为30秒)。 在超时到期的情况下,请求返回304状态代码(未修改),没有内容。

我现在所拥有的是:

// AppComponent.ts

getValue(){
    this.service.getValue().subscribe(
    data=>{
        console.log('Current value: ', data.value);
    },
    error=>{
        console.log('Error: Could not get the value.', error);
    }
    );
}

//服务

getValue():Observable<Interface>{
    let url = 'http://localhost/url';

    this.http.get(url)
    .map(res=>this.extractresponse)
    .catch(this.handleError)
}

  extractResponse(res: Response) {
    let body = res.json();
    return body;
  }

    handleError(error: any) {
    let errMsg = (error.message) ? error.message :
      error.status ? `${error.status} - ${error.statusText}` : 'Server error';
    console.error(errMsg); 
    return Observable.throw(errMsg);
  }

这为我提供了启动应用程序时获得的Response值。但它并没有真正更新变量。我可以使用超时,但我宁愿遵循接口的建议并比较哈希。

在这种情况下我会这样做:

// AppComponent.ts

private _hash:number = 0;
private _value: string ;

getValue(){
    this.service.getValue(hash).subscribe(
    data=>{
        this._hash = data.hash;
        this._value = data.value;
    }, 
    error=>{
        console.log('Error: Could not get the value.', error);
    }
    );

}

//服务

getValue(hash):Observable<Interface>{
    let url = 'http://localhost/url/?hash=' + hash;

    this.http.get(url)
    .map(this.extractResponse)
    .catch(this.handleError);

}

现在的问题是,我将新哈希分配给我的私有变量_hash,显示我只是使用类似“retryWhen”错误或304? 如果我重试并且没有错误。我怎么知道变量是否会再次更新?

谢谢!

1 个答案:

答案 0 :(得分:1)

如果我理解正确,您就会收到第一个请求的回复,但之后又没有再次更新?

调用subscribe()是触发http请求的内容,一旦完成,observable就完成了。除非再次调用subscribe(),否则它不会再次执行。如上所述,您可以通过使用计时器来解决这个问题,但是您不希望有延迟。我在长轮询中遇到了同样的问题,最后遇到了答案:expand()。

expand()的描述很难理解,它说它以递归方式调用投影函数。什么??但实际发生的是每次内部observable发出一个值时,外部observable发出相同的值,然后调用你的投影函数返回一个新的observable,然后将新的observable添加到内部池。这正是我们想要的长轮询,但它确实需要你稍微分开你的逻辑。

public pollValues(id: string): Observable<any> {
  let poll = (stateToken: string) => {
    let headers = new HttpHeaders({'x-state-token': stateToken});
    return this.http.get<any>('https://blahblah/service/' + id, {headers: headers}).pipe(
      retryWhen(errors => {
        console.log("Got error, retrying in 10 seconds")
        return errors.pipe(delay(10000))
      }),
      tap(response => console.log("Got response: ", response)),
    );
  };

  return poll('initial-state-token').pipe(
    expand(response => {
      console.log("Expanding with new token " + response.stateToken);
      return poll(response.stateToken);
    })
  );
}

如您所见,Observable分两步构建。首先,一个名为&#34; poll&#34;创建一个获取状态令牌并使用该令牌创建get请求。它还会在出现错误(延迟10秒)时添加重试,并记录到控制台。

然后通过调用该新函数创建初始请求。现在我们有一个Observable,当你订阅它时会发出一个http请求。但我们想用expand()添加递归部分。 Expand()会将现有的http Observable传递给您的订阅,然后它还会调用&#34;使用新令牌扩展&#34;的函数。该函数的返回值是另一个Observable。现在你明白我们为什么要拆分我们的逻辑..新的Observable是另一个http get但是带有新的状态令牌。 Expand()将获取新的http get并执行它(假设您仍然订阅了外部Observable)并在该输出上运行expand()。这是递归部分。

因此,这会创建一个长轮询的Observable,它在请求之间没有延迟。 Web服务本身提供延迟(长轮询服务的延迟)非常重要。

注意:这是为Angular 5编写的,但是你可以在Angular 2中应用相同的rxjs逻辑,如果你还在使用它。唯一的区别是你仍然需要你的response.json()步骤。