异步请求
Ajax相关
AJAX(Asynchronous JavaScript and XML)是一种用于在客户端和服务器之间进行异步数据交换的技术。通过 AJAX,可以在不刷新整个页面的情况下向服务器发送请求并获取数据,然后使用 JavaScript 更新页面的部分内容。
ajax返回的状态
Ajax 请求状态 readyState:
0:未初始化。表示 XMLHttpRequest 对象已创建,但尚未调用 open() 方法。1:正在加载。表示 open() 方法已被调用,但尚未调用 send() 方法。2:已加载。表示 send() 方法已被调用,且已收到响应头。3:交互中。表示正在接收响应体。4:完成。表示响应已完全接收。
let xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com/api/data', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log('请求成功');
console.log(xhr.responseText);
} else {
console.error('请求失败,状态码为:', xhr.status);
}
}
};
xhr.send();
实现一个 AJAX 请求
实现一个 AJAX 请求的基本步骤如下:
- 创建一个 XMLHttpRequest 对象(或使用新的 fetch API)。
- 设置请求的方法(GET、POST 等)和 URL。
- 可选地设置请求的头部信息。
- 注册一个回调函数来处理响应结果。
- 发送请求到服务器。
- 在回调函数中处理响应数据。
下面是一个使用原生 JavaScript 实现 AJAX 请求的示例:
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
var response = xhr.responseText;
console.log(response);
} else {
console.error('Error: ' + xhr.status);
}
}
};
xhr.send();
接下来,我们可以使用 Promise 对象来封装 AJAX 请求,以便更方便地处理异步操作。下面是使用 Promise 封装 AJAX 请求的示例:
function ajaxRequest(url, method) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
resolve(xhr.responseText);
} else {
reject(new Error('Request failed: ' + xhr.status));
}
}
};
xhr.send();
});
}
// 使用封装的 AJAX 请求
ajaxRequest('https://api.example.com/data', 'GET')
.then(function(response) {
console.log(response);
})
.catch(function(error) {
console.error(error);
});
在上述代码中,我们创建了一个名为 ajaxRequest 的函数,它返回一个 Promise 对象。在 Promise 的回调函数中,执行 AJAX 请求并根据请求的结果调用 resolve 或 reject。然后,可以使用 then 方法处理成功的响应结果,使用 catch 方法处理错误。这种封装可以更好地处理异步操作的结果,使代码更具可读性和可维护性。
写出原生Ajax某种顺序一次执行,有什么办法呢?如何处理ajax跨域
一、让多个 Ajax 按顺序依次执行
常见有三种方式:回调嵌套、Promise 链式调用、async/await。推荐使用 async/await,可读性最好。
- 回调嵌套(传统写法,容易产生"回调地狱"):
ajax(url1, function (res1) {
ajax(url2, function (res2) {
console.log(res1, res2);
});
});
- Promise 链式调用:
function ajax(url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) resolve(xhr.responseText);
else reject(new Error(xhr.status));
}
};
xhr.send();
});
}
ajax(url1)
.then(function (res1) {
console.log(res1);
return ajax(url2);
})
.then(function (res2) {
console.log(res2);
})
.catch(function (err) {
console.error(err);
});
- async/await(最推荐):
async function run() {
try {
var res1 = await ajax(url1);
console.log(res1);
var res2 = await ajax(url2);
console.log(res2);
} catch (err) {
console.error(err);
}
}
run();
二、如何处理 Ajax 跨域
浏览器的同源策略会限制不同源(协议、域名、端口任一不同)的请求,常见解决方案有:
- CORS(跨域资源共享):最主流方案。由服务器在响应头中设置
Access-Control-Allow-Origin等字段,浏览器便会放行跨域请求。对于 复杂请求会先触发一次OPTIONS预检请求。
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type
- JSONP:利用
<script>标签不受同源策略限制的特点,只能用于GET请求。前端声明回调函数,服务端返回一段调用该函数的脚本。
function handleData(data) {
console.log(data);
}
var script = document.createElement('script');
script.src = 'https://api.example.com/data?callback=handleData';
document.body.appendChild(script);
- 代理服务器:开发阶段常用 webpack-dev-server / vite 的
proxy配置,生产环境用 nginx 反向代理,把跨域请求转为同源请求转发到目标服务器。
// vite.config.js
export default {
server: {
proxy: {
'/api': {
target: 'https://api.example.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
};
如何实现一个ajax请求?如果我想发出两个有顺序的ajax需要怎么做?
一、实现一个 ajax 请求
使用原生 XMLHttpRequest 的完整步骤:
- 创建
XMLHttpRequest对象。 - 调用
open(method, url, async)初始化请求。 - 设置请求头(如需)。
- 监听
onreadystatechange或onload事件处理响应。 - 调用
send()发送请求,POST 请求在send中传入请求体。
function ajax(url, method, data) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open(method || 'GET', url, true);
// POST 请求需设置请求头
if ((method || 'GET').toUpperCase() === 'POST') {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
}
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.responseText);
} else {
reject(new Error('请求失败,状态码:' + xhr.status));
}
}
};
xhr.send(data || null);
});
}
二、发出两个有顺序的 ajax 请求
即第二个请求依赖第一个请求的结果,必须等第一个完成后再发起。可用 Promise .then 链或 async/await 实现:
- Promise 链式调用:
ajax('https://api.example.com/user', 'GET')
.then(function (res1) {
console.log('第一个请求结果:', res1);
var userId = JSON.parse(res1).id;
// 将第二个请求的 Promise 返回,形成链式调用
return ajax('https://api.example.com/order?userId=' + userId, 'GET');
})
.then(function (res2) {
console.log('第二个请求结果:', res2);
})
.catch(function (err) {
console.error(err);
});
- async/await(同步写法更直观):
async function getOrder() {
try {
var res1 = await ajax('https://api.example.com/user', 'GET');
var userId = JSON.parse(res1).id;
var res2 = await ajax('https://api.example.com/order?userId=' + userId, 'GET');
console.log(res1, res2);
} catch (err) {
console.error(err);
}
}
getOrder();
原生JS的ajax
原生 ajax 基于 XMLHttpRequest 对象,核心流程是 open → send → onreadystatechange 判断 readyState 与 status。
readyState 状态值说明:
| 值 | 状态 | 说明 |
|---|---|---|
| 0 | UNSENT | XMLHttpRequest 已创建,未调用 open |
| 1 | OPENED | open() 已调用 |
| 2 | HEADERS_RECEIVED | send() 已调用,响应头已接收 |
| 3 | LOADING | 正在接收响应体 |
| 4 | DONE | 响应全部接收完成 |
1. GET 请求
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data?id=1', true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log(xhr.responseText);
} else {
console.error('请求失败:', xhr.status);
}
}
};
xhr.send();
2. POST 请求
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://api.example.com/data', true);
// POST 需设置请求头
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText);
}
};
xhr.send('name=张三&age=18');
3. 使用 fetch 的写法
fetch 是基于 Promise 的现代 API,写法更简洁:
// GET
fetch('https://api.example.com/data?id=1')
.then(function (res) {
if (!res.ok) throw new Error('请求失败:' + res.status);
return res.json();
})
.then(function (data) {
console.log(data);
})
.catch(function (err) {
console.error(err);
});
// POST
fetch('https://api.example.com/data', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: '张三', age: 18 })
})
.then(function (res) { return res.json(); })
.then(function (data) { console.log(data); });
将原生的 ajax 封装成 promise
var myNewAjax = function (url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("get", url);
xhr.send(data);
xhr.onreadystatechange = function () {
if (xhr.status == 200 && readyState == 4) {
var json = JSON.parse(xhr.responseText);
resolve(json);
} else if (xhr.readyState == 4 && xhr.status != 200) {
reject("error");
}
};
});
};
FAQ?
Fetch和Ajax比有什么优缺点?
Fetch 和 XMLHttpRequest(Ajax) 都是浏览器原生提供的网络请求方案,两者对比如下:
一、Fetch 的优点
- 基于 Promise:天然支持 Promise 链式调用,配合 async/await 代码更简洁,避免了 XHR 的回调嵌套。
- API 更简洁:使用
fetch(url, options)一行即可发起请求,配置以对象形式传入,比 XHR 的open/send/setRequestHeader直观。 - 支持数据流:
Response.body是一个ReadableStream,可以分块读取大文件、实现流式处理,XHR 只能拿到完整响应体。 - 模块化设计:Request、Response、Headers 都是独立对象,便于在 Service Worker 中拦截和改写。
二、Fetch 的缺点
- 默认不带 Cookie:
fetch默认不会发送 cookie,需要手动配置credentials: 'include',而 XHR 默认会携带同 源 cookie。 - 只在网络错误时 reject:只要服务器返回了响应(哪怕 4xx、5xx),
fetch都会 resolve,需要手动判断res.ok或res.status才能捕获业务错误。
fetch(url).then(function (res) {
if (!res.ok) throw new Error(res.status); // 必须手动判断
return res.json();
});
- 不支持超时和进度事件:XHR 有
onprogress可监听上传/下载进度,fetch 原生没有进度事件。超时也需借助AbortController手动实现:
var controller = new AbortController();
var timer = setTimeout(function () { controller.abort(); }, 5000);
fetch(url, { signal: controller.signal })
.then(function (res) { clearTimeout(timer); return res.json(); })
.catch(function (e) { if (e.name === 'AbortError') console.log('请求超时'); });
- 不能同步请求:XHR 支持
open(method, url, false)同步请求(不推荐),fetch 只能异步。
三、对比总结
| 对比项 | Fetch | XMLHttpRequest |
|---|---|---|
| 返回形式 | Promise | 基于事件回调 |
| API 风格 | 简洁、配置式 | 繁琐、命令式 |
| 错误处理 | 仅网络错误 reject,HTTP 错误需手动判断 | 通过 status 直接判断 |
| Cookie | 默认不携带,需 credentials 配置 | 默认携带同源 cookie |
| 进度监听 | 无原生进度事件 | onprogress 支持上传/下载进度 |
| 取消请求 | AbortController | xhr.abort() |
| 超时控制 | 需配合 AbortController | xhr.timeout 直接设置 |
| 流式读取 | Response.body ReadableStream | 只能拿完整响应 |
总结:新项目推荐使用 fetch,API 更现代,配合 AbortController 即可补齐取消/超时能力;但若需要监听上传下载进度(如文件上传场景),XHR 仍是更合适的选择。
ajax、axios、fetch 的区别
ajax、axios 和 fetch 都是用于在 JavaScript 中进行异步网络请求的工具,它们之间有以下区别:
-
API 设计和语法:
ajax:ajax并不是一个具体的库或工具,而是指异步 JavaScript 和 XML。它是一种基于原生XMLHttpRequest对象实现的异步请求方法,没有统一的 API 设计,开发者需要手动编写请求代码和处理回 调。axios:axios是一个流行的第三方库,提供了简洁而强大的 API。它基于Promise实现,使用链式调用的方式进行请求配置和处理响应。fetch:fetch是现代浏览器提供的原生方法,用于进行网络请求。它基于 Promise 实现,使用 Promise 的方式进行请求配置和处理响应。
-
浏览器兼容性:
ajax:XMLHttpRequest是原生 JavaScript 提供的网络请求对象,具有广泛的浏览器兼容性。axios:axios是一个基于XMLHttpRequest的库,可以在现代浏览器和较旧的浏览器中使用。fetch:fetch是现代浏览器提供的原生方法,对于较旧的浏览器需要使用 polyfill 进行兼容处理。
-
功能和特性:
ajax:由于ajax并不是一个具体的库,它的功能和特性取决于开发者自己编写的代码和所使用的请求库。axios:axios提供了许多高级功能,如拦截器、请求和响应的转换、取消请求等。它还提供了更友好的错误处理机制。fetch:fetch是一个简单的 API,提供了基本的请求和响应功能,但在某些方面功能相对较弱,如没有原生的超时控制和请求取消机制。
-
请求和响应处理:
ajax和axios:ajax和axios都提供了丰富的配置选项,可以设置请求头、请求方法、请求体等。它们可以使用回调函数或 Promise 来处理响应结果。fetch:fetch使用 Promise 来处理响应,它返回一个 Promise 对象,可以使用then和catch方法来处理响应结果。但它的 API 设计相对较低级,使用起来不够简洁和友好。
综上所述,ajax 是基于原生 XMLHttpRequest 的异步请求方法,没有统一的 API 设计;axios 是一个功能强大、易用的第三方库,基于 XMLHttpRequest 实现;fetch 是现代浏览器原生提供的方法,使用 Promise 实现。