Skip to content

Instantly share code, notes, and snippets.

@teal-front
Last active March 28, 2024 02:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save teal-front/b523485d0da182a0bf6132c7691b4ed5 to your computer and use it in GitHub Desktop.
Save teal-front/b523485d0da182a0bf6132c7691b4ed5 to your computer and use it in GitHub Desktop.
project-snippets
/**
* fetch api
* 1. 确保返回 fulfilled promise,没有rejected promise,所以也不用处理错误
* 带错误的Promise<[null, data]>
* 正常结果的Promise<[{code: number, msg: string}]>
* 2. 使用了signal来处理超时情况
*/
// 如果是sdk项目,使用ponyfill,如果是自己的项目,使用polyfill(isomorphic-fetch)
import fetchPonyfill from "fetch-ponyfill";
import pickBy from "lodash/pickBy";
const {fetch} = fetchPonyfill();
const MAX_TIME = 30000;
request.get = function (path, payload = {}) {
return request(path, payload, "GET");
};
request.post = function (...args) {
return request(...args, "POST");
};
export function request(url, payload = {}, method = "POST") {
const controller = typeof AbortController === "function"
? new AbortController()
: {
abort: () => ({}),
signal: null,
};
const id = setTimeout(() => controller.abort(), MAX_TIME);
const requestOptions = {
headers: {
// 根据实际请求来添加自定义headers
"Content-Type": "application/json; charset=utf-8",
},
method,
signal: controller.signal,
};
if (method === "POST") {
requestOptions.body = payload instanceof FormData ? payload : JSON.stringify(payload);
} else if (method === "GET") {
const packPayload = pickBy(
payload,
v => !(v === undefined || v === null),
);
if (Object.keys(packPayload).length) {
url += `?${new URLSearchParams(packPayload)}`;
}
}
return fetch(url, requestOptions)
.then((res) => {
if (!res.ok) {
return res.json();
}
// 兼容blob类型的返回
// 当要手动处理下载的文件时,转换responseType为blob
return requestOptions.responseType === 'blob' ? res.blob() : res.json();
})
.then((data) => {
// 当要手动处理下载的文件时,直接返回blob类型的数据
if (data instanceof Blob) return [null, data];
if (data.retCode !== 0) {
throw data;
}
const {retCode, retMsg, ...restData} = data;
return [null, restData];
})
.catch(e => {
// 网络层的超时、CORS错误等会直接走到catch处理
// CORS错误name为TypeError
// 超时的错误nameAbortError
if (e instanceof Error) {
return [{code: -1, msg: e.message}, null];
}
// !res.ok从这里抛出的错误时,e为{code,msg}
if (e.code) {
return [e, null];
}
// 从data.retCode处返回的错误时,e为{retCode, retMsg}
return [{code: e.retCode, msg: e.retMsg || "网络错误,请稍后重试"}, null];
})
.finally(() => {
clearTimeout(id);
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment