傳送多個順序 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