发送多个顺序 HTTP 请求

制作一系列 HTTP 请求有两个主要原因:

  • 请求相互依赖(连续请求需要一个请求的结果)。

  • 我们希望将服务器负载分散到多个请求中。

1.发出多个相关请求

这可以使用 concatMap() 运算符来执行,以将一个响应转换为连续请求所需的参数。

function mockHTTPRequest(url) {
    return Observable.of(`Response from ${url}`)
      .delay(1000);
}

function timestamp() {
  return (new Date()).getTime() - start;
}

var start = (new Date()).getTime();

Observable.of('url-1') 
  // first request
  .concatMap(url => {
    console.log(timestamp() + ': Sending request to ' + url);
    return mockHTTPRequest(url);
  })
  .do(response => console.log(timestamp() + ': ' + response))

  // second request
  .concatMap(response => {
    console.log(timestamp() + ': Sending request to ' + response);
    let newUrl = 'url-' + response.length; // create new requests url
    return mockHTTPRequest(newUrl);
  })
  .do(response => console.log(timestamp() + ': ' + response))

  // third request
  .concatMap(response => {
    console.log(timestamp() + ': Sending request to ' + response);
    let newUrl = 'url-' + response.length; // create another requests url
    return mockHTTPRequest(newUrl);
  })
  .subscribe(response => console.log(timestamp() + ': ' + response));

运算符 concatMap() 在内部订阅 Observable 从其投影函数返回,并等待它完成,同时重新发出其所有值。

此示例为每个请求和响应添加时间戳:

3: Sending request to url-1
1010: Response from url-1
1011: Sending request to Response from url-1
2014: Response from url-19
2015: Sending request to Response from url-19
3017: Response from url-20

查看现场演示: https//jsbin.com/fewidiv/6/edit?js,console

处理错误

如果任何 HTTP 请求失败,我们显然不想继续,因为我们无法构造以下请求。

2.连续提出请求

如果我们对之前的 HTTP 请求的响应不感兴趣,我们可以只获取一个 URL 数组并一个接一个地执行它们。

function mockHTTPRequest(url) {
    return Observable.of(`Response from ${url}`)
      .delay(1000);
}

let urls = ['url-1', 'url-2', 'url-3', 'url-4'];
let start = (new Date()).getTime();

Observable.from(urls)
  .concatMap(url => mockHTTPRequest(url))
  .timestamp()
  .map(stamp => [stamp.timestamp - start, stamp.value])
  .subscribe(val => console.log(val));

此示例打印带时间戳的响应:

[1006, "Response from url-1"]
[2012, "Response from url-2"]
[3014, "Response from url-3"]
[4016, "Response from url-4"]

观看现场演示: https//jsbin.com/kakede/3/edit?js,console

延迟连续的通话

我们可能还想在每个请求之间做一个小延迟。在这种情况下,我们需要在每次 mockHTTPRequest() 调用后附加 delay()

Observable.from(urls)
  .concatMap(url => {
    return mockHTTPRequest(url)
      .do(response => console.log(((new Date()).getTime() - start) + ': Sending request to ' + url))
      .delay(500);
  })
  .timestamp()
  .map(stamp => [stamp.timestamp - start, stamp.value])
  .subscribe(val => console.log(val));

这将打印到控制台以下输出:

2024: Sending request to url-1
[2833, "Response from url-1"]
4569: Sending request to url-2
[5897, "Response from url-2"]
7880: Sending request to url-3
[8674, "Response from url-3"]
9789: Sending request to url-4
[10796, "Response from url-4"]

观看现场演示: https//jsbin.com/kakede/4/edit?js,console

处理错误

如果我们只是想在任何 HTTP 请求失败时忽略,我们必须链接 catch() ofter mockHTTPRequest()

function mockHTTPRequest(url) {
  if (url == 'url-3') {
    return Observable.throw(new Error(`Request ${url} failed.`));
  } else {
    return Observable.of(`Response from ${url}`)
      .delay(1000);
  }
}

let urls = ['url-1', 'url-2', 'url-3', 'url-4'];
let start = (new Date()).getTime();

Observable.from(urls)
  .concatMap(url => mockHTTPRequest(url).catch(obs => Observable.empty()))
  .timestamp()
  .map(stamp => [stamp.timestamp - start, stamp.value])
  .subscribe(val => console.log(val));

这只是忽略了 url-3 的调用:

[1004, "Response from url-1"]
[2012, "Response from url-2"]
[3016, "Response from url-4"]

查看现场演示: https//jsbin.com/jowiqo/2/edit?js,console

如果我们没有使用 catch() 运算符,则 url-3 会导致链发送错误通知,并且最后的 url-4 将不会被执行。

查看现场演示: https//jsbin.com/docapim/3/edit?js,console