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 的所有功能都是使用管道實現的,包括 baseURL,searchParams,超時和重試等等。
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>
它的工作流程為:
管道的位置有三種,分別是 Params 管道,Request 管道和 Response 管道。
函數參數將通過 Params 管道進行處理,並通過 new Request(input, init) 構造請求;請求接着通過 Request 管道,並使用 fetch(request) 得到響應;響應經過 Response 管道處理成為最終的返回值。
每個位置還有三種管道類型,分別為普通管道,錯誤管道和工廠管道。
函數正常運行時,數據將流經普通管道並經由它們處理;當函數發生錯誤時,拋出的錯誤將由錯誤管道處理。工廠管道接收 input 和 init 並返回一個普通管道,這在需要根據參數動態構造管道時十分有用。
例如,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。