本文已参与「新人创作礼」活动,一起开启掘金创作之路。
手动实现一个简单的ajax, 通过xhr对象,去写个简单的流程。
javascript function ajax(url,onSuccess,onFailed) { const xhr = window.XMLHTTPRequest ? new XMLHTTPRequest() : new ActiveXObject(); xhr.open("get",url,true); xhr.send(); xhr.onreadystatechange = function () { if(xhr.readyState === 4) { if(xhr.status === 200) { onSuccess && onSuccess(xhr.responseText); } else { onFailed && onFailed(); } } } }
封装请求的拦截
添加一些请求的拦截,设置请求超时时间,处理请求成功时的响应,
请求的拦截一般干什么? 请求的拦截一般做一些公共校验,如果通过返回config,resolve后继续往下链
响应的拦截一般干什么? 拦截错误,公共的请求报错,处理返回数据等
```js // ajax.html const adaptor = function (config) { return new Promise((resolve, reject) => { const xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Mirosoft.XMLHttp"); xhr.open(config?.method.toUpperCase(), config?.url, true); xhr.send();
xhr.timeout = config?.timeout;
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(xhr.responseText);
} else {
reject("request error");
}
}
};
});
};
// 处理请求成功时的响应 const dispatchRequest = function (config) { return adaptor(config).then( function (res) { return res; }, function (reason) { return Promise.reject(reason); } ); };
const req = function (config) { const chain = [dispatchRequest, undefined];
if (config?.interceptor) {
chain.unshift(
config?.interceptor?.fullfilled,
config?.interceptor?.rejected
);
}
if (config?.adaptor) {
chain.push(config?.adaptor?.fullfilled, config?.adaptor?.rejected);
}
let promise = Promise.resolve(config);
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
return promise;
};
req({ method: "get", url: "http://127.0.0.1:5500/data.json", interceptor: { fullfilled: function (e) { console.log("请求被拦截住了", e); return e; // 一定要把config返回 }, }, adaptor: { fullfilled: function (e) { console.log("响应被拦截住了", e); return e; }, }, });
// data.json { "code": 0, "data": "返回数据了", "message": "成功", "success": true }
```
结果:
一定要把config返回,因为整体是链式结构的。resovePromise会把return的值resolve掉,作为接下来.then的result,否则拦截过后的页面的请求将获取不到数据和错误原因。
- 实现一种请求的取消 写一个on/emit去绑定事件,在处理请求的时候处理,从外部得到的config.cancel,在这里用on绑定了abort事件为,cancel传进来的函数,接收一个参数,作为cancel的参数,然后请求中emit执行abort,并将取消请求的方法,报错等等作为一个参数传入emit,而我们最终的目的就是能在外部,去调用这个参数,所以当emit的时候,把取消请求的方法传入绑定的事件中,又通过刚刚on那里穿到cancel,这个时候我们只要将cancel回调中的参数执行就可以取消请求。
我们还做了request的拦截和response的拦截,将请求的响应导入到一个数组chain,将request的拦截放到请求响应的前面,将response的拦截响应放到请求响应之后,这样[interceptorFullFilled,interceptorFullRejected,fullFilled,rejected,adAptorFullfilled,adAptorRejected] 然后我们从数组的前面往后两个一组执行,就可以让请求依次执行request的拦截,请求,response的拦截。
``` let cc = null;
const mitt = { cache: {}, on: function (name, func) { this.cache[name] = func; }, emit: function (name, data) { const fn = this.cache[name]; fn && fn(data); }, };
const adaptor = function (config) { return new Promise((resolve, reject) => { let xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Mirosoft.XMLHttp");
xhr.open(config?.method.toUpperCase(), config?.url, true); xhr.send();
xhr.onabort = function () { xhr = null; reject("[abort] request is aborted"); };
if (config.cancel) { mitt.emit("abort", function () { xhr.abort(); xhr = null; reject("【abort】current request is abort."); }); }
xhr.timeout = config?.timeout; xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status === 200) { setTimeout(function () { resolve(xhr && xhr.responseText); }, 5000); } else { reject("request error"); } } }; }); };
// 处理请求成功时的响应 const dispatchRequest = function (config) { return adaptor(config).then( function (res) { return res; }, function (reason) { return Promise.reject(reason); } ); };
const req = function (config) { const chain = [dispatchRequest, undefined];
if (config?.interceptor) { chain.unshift( config?.interceptor?.fullfilled, config?.interceptor?.rejected ); }
if (config?.adaptor) { chain.push(config?.adaptor?.fullfilled, config?.adaptor?.rejected); }
if (config.cancel) { mitt.on("abort", function (func) { config.cancel(func); }); }
let promise = Promise.resolve(config);
while (chain.length) { promise = promise.then(chain.shift(), chain.shift()); }
return promise; };
req({ method: "get", url: "http://127.0.0.1:5500/3browser/01/data.json", interceptor: { fullfilled: function (e) { console.log("请求被拦截住了", e); return e; // 一定要把config返回 }, }, adaptor: { fullfilled: function (e) { console.log("响应被拦截住了", e); return e; }, }, cancel: function (onCancel) { cc = onCancel; }, }); setTimeout(function () { cc && cc(); }, 2000);
```