Stories

Detail Return Return

RxJS + Fetch: HTTP 請求的新紀元 - Stories Detail

RxJS + Fetch: HTTP 請求的新紀元

Reach 是一個使用 RxJS 和原生 Fetch API 構建的 HTTP Client,它受到 Axios 的啓發。

RxJS 用於實現類似於 Axios 中攔截器的功能,它被稱作管道,是比攔截器更加強大的自定義工具。同時它也儘量使用 Web API 而不是自定義數據類型,這使得它十分的輕量。

它使用起來類似這樣:

import {
  RxFetch,
  baseURLKey,
  baseURLPipe,
  retryKey,
  retryPipe,
  timeoutKey,
  timeoutPipe,
  paramKey,
  dataSerialPipe,
  searchParamsPipe,
} from "@tbontb/reach";

const client = new RxFetch({ [baseURLKey]: "https://httpbin.org" });

client.pipes.params
  .use(baseURLPipe)
  .use(searchParamsPipe)
  .use(dataSerialPipe);

client.pipes.response.factory
  .add(timeoutPipe)
  .add(retryPipe);

client
  .get("uuid")
  .then((r) => r.json())
  .then((v) => console.log(v));

client
  .post(
    "delay/1",
    { name: "user name" }, // data
    {
      [timeoutKey]: 3000,
      [paramKey]: { id: "123" },
      [retryKey]: { count: 2, delay: 1000 },
    } // init
  )
  .then((r) => r.json())
  .then((v) => console.log(v));

在這裏我們創建了一個 HTTP Client,並添加了一些管道(相當於 Axios 的攔截器)。管道可以讓我們對請求參數, Request 和 Response 進行操作,Reach 的所有功能都是使用管道實現的,包括 baseURLsearchParams,超時和重試等等。

HTTP 請求方法的函數簽名和原生 Fetch API 一致,其中 post/put/patch 請求方法多了一個 data 參數。

client.get(
  input: string | URL | Request,
  init?: RequestInit
) : Promise<Response>

client.post(
  input: string | URL | Request,
  data: unknown,
  init?: RequestInit
) : Promise<Response>

它的工作流程為:

image.png

管道的位置有三種,分別是 Params 管道,Request 管道和 Response 管道。

函數參數將通過 Params 管道進行處理,並通過 new Request(input, init) 構造請求;請求接着通過 Request 管道,並使用 fetch(request) 得到響應;響應經過 Response 管道處理成為最終的返回值。

每個位置還有三種管道類型,分別為普通管道,錯誤管道和工廠管道。

函數正常運行時,數據將流經普通管道並經由它們處理;當函數發生錯誤時,拋出的錯誤將由錯誤管道處理。工廠管道接收 inputinit 並返回一個普通管道,這在需要根據參數動態構造管道時十分有用。

例如,baseURLPipe 是一個 Params 普通管道,它的實現如下:

const baseURLKey = Symbol("baseURL");

declare global {
  interface RequestInit {
    [baseURLKey]?: string | URL;
  }
}

RxFetch.init[baseURLKey] = location.origin;

const baseURLPipe: ParamsPipe = ([input, init]) => {
  if (typeof input === "string" && !URL.canParse(input))
    return [new URL(input, init[baseURLKey]), init];
};

當 input 的類型為 string 且不是合法的 URL 時,管道將使用 baseURL 構造合法的 URL 返回。

retryPipe 是一個 Response 工廠管道,它使用 init 中的 timeout 屬性和 RxJS 中的 retry 操作符構造一個 Response 普通管道,用於在請求出錯時重試。

import { retry, RetryConfig } from "rxjs";

const retryKey = Symbol("retry");

declare global {
  interface RequestInit {
    [retryKey]?: number | RetryConfig;
  }
}

RxFetch.init[retryKey] = { count: 2, delay: 500 };

const retryPipe: ResponseFactory = (_, init) => retry(<number>init[retryKey]);

你可能會注意到上面的代碼中使用 Symbol 來定義我們的鍵,這是因為 RequestInit 並不是我們自定義的新數據類型,而是原生的 Web API。為了防止我們對 Web API 的拓展與將來的標準產生衝突,我們需要用這種方式定義鍵。

當前項目還處於起步階段,如果你有好的想法,可以提 issue 或者 PR,項目地址在 https://gitlab.com/tbontb-iaq/reach。

user avatar toopoo Avatar user_2dx56kla Avatar linlinma Avatar yinzhixiaxue Avatar jcguanqi Avatar littlelyon Avatar zaoying Avatar linx Avatar anchen_5c17815319fb5 Avatar qian5201314 Avatar shuirong1997 Avatar jiavan Avatar
Favorites 98 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.