跳到主要内容

语法

2023年07月14日
柏拉文
越努力,越幸运

一、GET


function normalizeUrl(url, params) {
let queryString = Object.keys(params).reduce((prev, key) => {
return (prev += `${key}=${params[key]}&`);
}, "?");
return url + queryString.slice(0, queryString.length - 1);
}

function get(url, params) {
return new Promise((resolve, reject) => {
const normalizedUrl = normalizeUrl(url, params);
const ajax = new XMLHttpRequest();
ajax.open("GET", normalizedUrl);
ajax.addEventListener("readystatechange", function (event) {
if (ajax.readyState === 4 && [200, 304].includes(ajax.status)) {
resolve(JSON.parse(ajax.responseText));
}
});
ajax.send();
});
}

get("http://test.bolawen.com/server/sync", { a: 1, b: 2 })
.then((res) => {
console.log("res", res);
})
.catch((error) => {
console.log("error", error);
});

二、POST


2.1 text/plain

Content-Type: text/plain

数据格式

数据请求

2.2 application/json

Content-Type: application/json JSON 是一种轻量级的数据格式,以键-值对的方式组织的数据。这个使用这个类型,需要参数本身就是json格式的数据,参数会被直接放到请求实体里,不进行任何处理。服务端/客户端会按json格式解析数据(约定好的情况下)。

数据格式

"{ "a": 1, "b": 2 }"

数据请求

function normalizeToJson(params){
return JSON.stringify(params)
}

function post(url, params) {
const normalizedParams = normalizeToJson(params);
return new Promise((resolve, reject) => {
const ajax = new XMLHttpRequest();
ajax.open("POST", url, true);
ajax.addEventListener("readystatechange", function () {
if (ajax.readyState === 4 && [200, 304].includes(ajax.status)) {
return resolve(JSON.parse(ajax.responseText));
}
});
ajax.send(normalizedParams);
});
}

post("http://test.bolawen.com/server/sync", { a: 1, b: 2 })
.then((res) => {
console.log("res", res);
})
.catch((error) => {
console.log("error", error);
});

2.3 multipart/form-data

Content-Type: multipart/form-data

数据格式

  • 普通数据

    Form Data {
    a: 1
    b: 2
    }
  • 文件数据

    ------
    ------

数据请求

function normalizeToFormData(params) {
const formData = new FormData();
Object.keys(params).forEach((key) => {
formData.append(key, params[key]);
});
return formData;
}

function post(url, params) {
const normalizedParams = normalizeToFormData(params);
return new Promise((resolve, reject) => {
const ajax = new XMLHttpRequest();
ajax.open("POST", url, true);
ajax.addEventListener("readystatechange", function () {
if (ajax.readyState === 4 && [200, 304].includes(ajax.status)) {
return resolve(JSON.parse(ajax.responseText));
}
});
ajax.send(normalizedParams);
});
}

post("http://test.bolawen.com/server/sync", { a: 1, b: 2 })
.then((res) => {
console.log("res", res);
})
.catch((error) => {
console.log("error", error);
});

2.4 application/x-www-form-urlencoded

Content-Type: application/x-www-form-urlencoded

数据格式

a=1&b=2&c=3

数据请求

function normalizeToFormUrlencoded(params) {
const searchParams = new URLSearchParams();
Object.keys(params).forEach(key=>{
searchParams.append(key,params[key]);
});
return searchParams.toString();
}

function post(url, params) {
const normalizedParams = normalizeToFormUrlencoded(params);
return new Promise((resolve, reject) => {
const ajax = new XMLHttpRequest();
ajax.open("POST", url, true);
ajax.addEventListener("readystatechange", function () {
if (ajax.readyState === 4 && [200, 304].includes(ajax.status)) {
return resolve(JSON.parse(ajax.responseText));
}
});
ajax.send(normalizedParams);
});
}

post("http://test.bolawen.com/server/sync", { a: 1, b: 2 })
.then((res) => {
console.log("res", res);
})
.catch((error) => {
console.log("error", error);
});

三、Abort


function normalizeUrl(url, params) {
let queryString = Object.keys(params).reduce((prev, key) => {
return (prev += `${key}=${params[key]}&`);
}, "?");
return url + queryString.slice(0, queryString.length - 1);
}

function normalizeToFormUrlencoded(params) {
const searchParams = new URLSearchParams();
Object.keys(params).forEach((key) => {
searchParams.append(key, params[key]);
});
return searchParams.toString();
}

function normalizeToFormData(params) {
const formData = new FormData();
Object.keys(params).forEach((key) => {
formData.append(key, params[key]);
});
return formData;
}

function normalizeToJson(params) {
return JSON.stringify(params);
}

function normalizeBody(headers, params) {
if (!headers) {
return normalizeToJson(params);
}
const contentType =
headers["Content-Type"] ||
headers["Content-type"] ||
headers["content-type"];
if (!contentType || contentType === "application/json") {
return normalizeToJson(params);
} else if (contentType === "x-www-form-urlencoded") {
return normalizeToFormUrlencoded(params);
} else {
return normalizeToFormData(params);
}
}

function request(url, method, params, headers) {
let ajax = new XMLHttpRequest();
const baseRequest = () => {
return new Promise((resolve, reject) => {
const normalizeMethod = method.toUpperCase();
const normalizedUrl =
normalizeMethod === "GET" ? normalizeUrl(url, params) : url;
ajax.open(normalizeMethod, normalizedUrl);
ajax.addEventListener("readystatechange", function () {
if (ajax.readyState === 4 && [200, 304].includes(ajax.status)) {
return resolve(JSON.parse(ajax.responseText));
}
});
ajax.addEventListener("error", function () {
return reject("请求已出错");
});
ajax.addEventListener("abort", function () {
return reject(`请求已中断`);
});
const normalizedBody =
normalizeMethod === "GET" ? null : normalizeBody(headers, params);
ajax.send(normalizedBody);
});
};
return {
ajax,
run: baseRequest,
};
}

const { ajax, run } = request("http://test.bolawen.com/server/async", "post", {
a: 1,
b: 2,
});
run()
.then((res) => {
console.log(res);
})
.catch((error) => {
console.log(error);
});

setTimeout(() => {
ajax.abort();
}, 3000);

四、Progress


4.1 上传

  • ajax.upload.progress: 检索的数据量发生了变化

  • ajax.upload.load: 传输完成,所有数据保存在 response

let ajax;
let oldTime = 0;
let oldLoaded = 0;

const fileDom = document.getElementById("fileElm");
const processDom = document.getElementById("process");
const cancelUploadDom = document.getElementById("cancel-upload");
const restTimeTextDom = document.getElementById("rest-time-text");
const uploadSpeedTextDom = document.getElementById("upload-speed-text");

function normalizeToFormData(params) {
const formData = new FormData();
Object.keys(params).forEach((key) => {
formData.append(key, params[key]);
});
return formData;
}

function uploadLoadStart() {
oldTime = new Date().getTime();
oldLoaded = 0;
}

function transformRestTimeText(event, diffLoaded, diffTime) {
const uploadSpeed = diffLoaded / diffTime;
let restTime = (event.total - event.loaded) / uploadSpeed;
let units = "s";
if (restTime / 60 > 1) {
restTime /= 60;
units = "m";
}
if (restTime / 60 > 1) {
restTime /= 60;
units = "h";
}
restTime = restTime.toFixed(1);
restTimeTextDom.innerHTML = "剩余时间" + restTime + units;
}

function transformUploadSpeedText(diffLoaded, diffTime) {
let uploadSpeed = diffLoaded / diffTime;
let units = "b/s";
if (uploadSpeed / 1024 > 1) {
uploadSpeed = uploadSpeed / 1024;
units = "k/s";
}
if (uploadSpeed / 1024 > 1) {
uploadSpeed = uploadSpeed / 1024;
units = "M/s";
}
uploadSpeed = uploadSpeed.toFixed(1);
uploadSpeedTextDom.innerHTML = "上传速度: " + uploadSpeed + units;
}

function uploadProgress(event) {
if (event.lengthComputable) {
const percentage = Math.round((event.loaded * 100) / event.total);
processDom.value = percentage;
}
const nowTime = new Date().getTime();
const diffTime = (nowTime - oldTime) / 1000;
oldTime = nowTime;
const diffLoaded = event.loaded - oldLoaded;
oldLoaded = event.loaded;
transformUploadSpeedText(diffLoaded, diffTime);
transformRestTimeText(event, diffLoaded, diffTime);
}

function uploadLoad(event) {
process.value = 100;
restTimeTextDom.innerHTML = "已完成";
}

function uploadCancel() {
restTimeTextDom.innerHTML = "";
uploadSpeedTextDom.innerHTML = "";
}

function request(url, params) {
return new Promise((resolve, reject) => {
ajax = new XMLHttpRequest();
ajax.open("POST", url, true);
ajax.upload.addEventListener("loadstart", uploadLoadStart);
ajax.upload.addEventListener("progress", uploadProgress);
ajax.upload.addEventListener("load", uploadLoad);
ajax.addEventListener("readystatechange", function () {
if (ajax.readyState === 4 && [200, 304].includes(ajax.status)) {
return resolve(JSON.parse(ajax.responseText));
}
});
ajax.addEventListener("error", function () {
return reject("请求已出错");
});
ajax.addEventListener("abort", function () {
uploadCancel();
return reject("已取消上传");
});
ajax.send(normalizeToFormData(params));
});
}

function selectFile(e) {
const {
target: { files },
} = e;
request("http://test.bolawen.com/server/async", { file: files[0] })
.then((res) => {
console.log(res);
})
.catch((error) => {
console.log(error);
});
}

fileDom.addEventListener("change", selectFile);
cancelUploadDom.addEventListener("click", function () {
ajax.abort();
});

4.2 下载

  • ajax.progress

  • ajax.load

补充服务


Koa Serve

const Koa = require("koa");
const KoaRouter = require("koa-router");
const { koaBody } = require("koa-body");

const app = new Koa();
const router = new KoaRouter({ prefix: "/" });

function wait(time) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, time);
});
}

router.get("sync", async (ctx) => {
ctx.body = {
code: 200,
msg: "get 同步请求成功!",
data: ctx.request.query,
};
});

router.get("async", async (ctx) => {
await wait(8000);
ctx.body = {
code: 200,
msg: "get 异步请求成功!",
data: ctx.request.query,
};
});

router.post("sync", async (ctx) => {
ctx.body = {
code: 200,
msg: "post 同步请求成功!",
data: ctx.request.body,
};
});

router.post("async", async (ctx) => {
await wait(8000);
ctx.body = {
code: 200,
msg: "post 异步请求成功!",
data: ctx.request.body,
};
});

app.use(
koaBody({
text: true,
json: true,
multipart: true,
urlencoded: true,
})
);
app.use(router.routes());

app.listen("4000", () => {
console.log("服务启动成功!");
});