@exabytellc/utils
Version:
EB react utils to make everything a little easier!
222 lines (202 loc) • 5.88 kB
JavaScript
import { round } from "../math";
export default class HttpRequest {
// const
#states = {
0: "unsent",
1: "opened",
2: "uploading",
3: "processing",
4: "downloading",
5: "completed",
};
get requestStates() { return this.#states; }
// params
#request = new XMLHttpRequest();
#method = null;
#address = null;
#headers = null;
#urlData = null;
#bodyData = null;
#responseType = null;
#onStateChange = null;
// output
#httpCode = null;
#state = null;
#stateText = null;
#error = null;
#response = null;
#currentStats = {
0: null,
1: null,
2: null,
3: null,
4: null,
5: null,
};
#uploadCalc = {
start: null,
total: 0,
loaded: 0,
};
#upload = {
duration: 0,
progress: 0,
speed: 0,
eta: 0,
}
#downloadCalc = {
start: null,
total: 0,
loaded: 0,
};
#download = {
duration: 0, // milliseconds.
progress: 0, // %
speed: 0, // bytes per millisecond
eta: 0, // milliseconds.
}
// getters
get request() { return this.#request; }
get httpCode() { return this.#httpCode; }
get currentState() { return this.#state; }
get currentStateText() { return this.#stateText; }
get error() { return this.#error; }
get response() { return this.#response; }
get currentStats() { return this.#currentStats; }
get currentUpload() { return this.#upload; }
get currentDownload() { return this.#download; }
// constructor
constructor({
method = "GET",
address = null,
headers = null,
urlData = null,
bodyData = null,
responseType = "json",
onStateChange = null,
} = {}) {
this.#method = method;
this.#address = address;
this.#headers = headers;
this.#urlData = urlData;
this.#bodyData = bodyData;
this.#responseType = responseType;
this.#onStateChange = onStateChange;
this.#buildUrlWithQueryParams();
this.#setState(0, null);
}
async send() {
this.#configureRequest();
this.#registerEventListeners();
await this.#handleResponse();
}
#buildUrlWithQueryParams() {
if (this.#urlData) {
const queryParams = new URLSearchParams(this.#urlData).toString();
this.#address += `?${queryParams}`;
}
}
#configureRequest() {
if (this.#responseType) {
this.#request.responseType = this.#responseType;
}
this.#request.open(this.#method, this.#address, true);
if (this.#headers) {
for (const [key, value] of Object.entries(this.#headers)) {
this.#request.setRequestHeader(key, value);
}
}
}
#registerEventListeners() {
this.#request.onloadstart = () => this.#setState(1, null);
this.#request.upload.onloadstart = (event) => {
if (this.#onStateChange) this.#calcTransferRate(this.#uploadCalc, this.#upload, event.total, event.loaded);
this.#setState(2, null);
};
this.#request.upload.onprogress = (event) => {
if (this.#onStateChange) this.#calcTransferRate(this.#uploadCalc, this.#upload, event.total, event.loaded);
this.#setState(2, null);
};
this.#request.upload.onloadend = (event) => {
if (this.#onStateChange) this.#calcTransferRate(this.#uploadCalc, this.#upload, event.total, event.loaded);
this.#setState(3, null);
};
this.#request.onprogress = (event) => {
if (this.#onStateChange) this.#calcTransferRate(this.#downloadCalc, this.#download, event.total, event.loaded);
this.#setState(4, null);
};
this.#request.ontimeout = () => {
this.#setState(5, "timeout");
};
this.#request.onabort = () => {
this.#setState(5, "aborted");
};
this.#request.onerror = () => {
this.#setState(5, "failed");
};
}
async #handleResponse() {
return new Promise((resolve) => {
this.#request.onloadend = (event) => {
if (this.#onStateChange) this.#calcTransferRate(this.#downloadCalc, this.#download, event.total, event.loaded);
this.#response = this.#request.response;
this.#setState(5, null);
resolve();
};
this.#request.send(this.#bodyData);
});
}
#calcTransferRate(calcObj, outObj, total, loaded) {
calcObj.total = total;
calcObj.loaded = loaded;
outObj.progress = round(((calcObj.loaded / calcObj.total) * 100), 2);
if (!calcObj.start) {
calcObj.start = new Date().getTime();
} else {
outObj.duration = new Date().getTime() - calcObj.start;
if (outObj.progress == 100) {
outObj.speed = 0;
outObj.eta = 0;
} else {
outObj.speed = round((calcObj.loaded / outObj.duration) || 0, 2);
outObj.eta = round(((calcObj.total - calcObj.loaded) / outObj.speed) || 0, 2);
}
}
}
#setState(state, error = null) {
this.#httpCode = this.#request.status;
this.#state = state;
this.#stateText = this.#states[state];
this.#error = error;
if (!this.#currentStats[state]) {
this.#currentStats[state] = new Date().getTime();
if (state > 0) {
this.#currentStats[state - 1] =
this.#currentStats[state] - (this.#currentStats[state - 1] ?? 0);
}
}
if (this.#onStateChange) this.#onStateChange(this);
}
static async quickCall({
method = null,
address = null,
headers = null,
urlData = null,
bodyData = null,
responseType = null,
onStateChange = null
}) {
const request = new HttpRequest({
method,
address,
headers,
urlData,
bodyData,
responseType,
onStateChange
});
await request.send();
const res = request?.response;
return res;
}
}