import { objectKeys } from './utils/native';
/**
* @see {@link https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest | XMLHttpRequest}
* @typedef {Object} AjaxOptions ajax 配置项
* @property {string} [method="get"] 创建请求时使用的方法
* @property {boolean} [async=true] 是否异步执行操作
* @property {string|null} [user=null] 用户名,用于认证用途
* @property {string|null} [password=null] 密码,用于认证用途
* @property {Object} [headers] 自定义请求头
* @property {string} [responseType] 响应类型
* @property {number} [timeout] 请求超时的毫秒数
* @property {boolean} [withCredentials=false] 跨域请求时是否需要使用凭证
* @property {*} [data=null] 请求体被发送的数据
* @property {function} [onReadyStateChange] 当 readyState 属性发生变化时触发
* @property {function} [onLoadStart] 接收到响应数据时触发
* @property {function} [onProgress] 请求接收到更多数据时,周期性地触发
* @property {function} [onAbort] 当 request 被停止时触发,例如当程序调用 XMLHttpRequest.abort() 时
* @property {function} [onTimeout] 在预设时间内没有接收到响应时触发
* @property {function} [onError] 当 request 遭遇错误时触发
* @property {function} [onLoad] 请求成功完成时触发
* @property {function} [onLoadEnd] 请求结束时触发,无论请求成功 (load) 还是失败 (abort 或 error)
*/
/**
* 请求
*
* <em style="font-weight: bold;">注意:该方法仅适用于浏览器端。</em>
*
* @alias module:Browser.ajax
* @since 4.16.0
* @see {@link https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest | XMLHttpRequest}
* @param {string} url 地址
* @param {AjaxOptions} [options] 配置项
* @param {string} [options.method="get"] 创建请求时使用的方法。默认 `'get'`。
* @param {boolean} [options.async=true] 是否异步执行操作。默认 `true`。
* @param {string|null} [options.user=null] 用户名,用于认证用途。默认 `null`。
* @param {string|null} [options.password=null] 密码,用于认证用途。默认 `null`。
* @param {Object} [options.headers] 自定义请求头。
* @param {string} [options.responseType] 响应类型。
* @param {number} [options.timeout] 请求超时的毫秒数。
* @param {boolean} [options.withCredentials=false] 跨域请求时是否需要使用凭证。默认 `false`。
* @param {*} [options.data=null] 请求体被发送的数据。默认 `null`。
* @param {function} [options.onReadyStateChange] 当 readyState 属性发生变化时触发。
* @param {function} [options.onLoadStart] 接收到响应数据时触发。
* @param {function} [options.onProgress] 请求接收到更多数据时,周期性地触发。
* @param {function} [options.onAbort] 当 request 被停止时触发,例如当程序调用 XMLHttpRequest.abort() 时。
* @param {function} [options.onTimeout] 在预设时间内没有接收到响应时触发。
* @param {function} [options.onError] 当 request 遭遇错误时触发。
* @param {function} [options.onLoad] 请求成功完成时触发。
* @param {function} [options.onLoadEnd] 请求结束时触发,无论请求成功 (load) 还是失败 (abort 或 error)。
* @returns {Promise<object>} XHR 事件对象。
* @example
* ajax('/somefile').then(res=>{
* // do something
* });
*
* ajax('/api', { method: 'post' }).then(res=>{
* // do something
* });
*/
function ajax(url, options) {
const { method = 'get', data = null, timeout, headers, withCredentials = false, async = true, user = null, password = null, responseType, onReadyStateChange, onLoadStart, onProgress, onAbort, onTimeout, onError, onLoad, onLoadEnd } = options || {};
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method.toLowerCase(), url, async, user, password);
if (onReadyStateChange) {
xhr.onreadystatechange = onReadyStateChange;
}
// 设置请求超时
if (typeof timeout === 'number' && timeout > 0) {
xhr.timeout = timeout;
}
// 跨域请求时是否需要使用凭证
xhr.withCredentials = withCredentials;
// 设置响应类型
if (responseType) {
xhr.responseType = responseType;
}
// 设置请求头
if (typeof headers === 'object') {
objectKeys(headers).map((item) => {
xhr.setRequestHeader(item, headers[item]);
});
}
// 请求成功异步调用
const wrapSuccess = (cb) => {
// 内部方法
return (e) => {
resolve(e);
cb?.call(xhr, e);
};
};
// 请求失败(中断/超时/失败)处理
const wrapError = (cb) => {
// 内部方法
return (e) => {
reject(e);
cb?.call(xhr, e);
};
};
// 事件处理
const events = {
loadstart: onLoadStart,
progress: onProgress,
abort: wrapError(onAbort),
timeout: wrapError(onTimeout),
error: wrapError(onError),
load: wrapSuccess(onLoad),
loadend: onLoadEnd
};
const eventKeys = objectKeys(events);
eventKeys.map((item) => {
const func = events[item];
if (func) {
xhr.addEventListener(item, func);
}
});
xhr.send(data);
});
}
export default ajax;