引言
手写ajax一直是前端面试中的高频考点。本篇文章将带你使用Promise封装ajax请求。本篇会涉及到fetch和Promise的一些基础,这也能帮助我们更进一步理解实现ajax的过程。
fetch
在手写ajax之前,我们先来了解我们要实现的fetch。
fetch是一种现代的、更强大的用于发起网络请求的API,它提供了比传统的XMLHttpRequest更简洁和灵活的接口。fetch返回一个Promise,这使得它更容易处理异步操作,并且与async/await语法无缝集成。
我们在本篇文章中访问的地址,这是github开源项目中团队 "lemoncode" 的成员列表 api.github.com/orgs/lemonc…
这是网站地址中的JSON结构
下面是fetch的简单使用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul id="members">
</ul>
<script>
// 函数,url,GET fetch 新的API 不属于html5也不属于es6 基于Promise
// js 主动发起http 请求 fetch XMLHttpRequest实现
fetch('https://api.github.com/orgs/lemoncode/members')
.then(res => res.json()) // 二进制转为JSON 异步
.then(data=>{
// console.log(data);
document.getElementById('members').innerHTML = data.map(member=>`
<li>
<a href="${member.html_url}">${member.login}</a>
</li>
`).join('')
})
</script>
</body>
</html>
Promise
fetch的实现基于了es6的Promise,它返回了一个Promise对象。为了实现手写ajax,我们还需要了解Promise。
Promise是 JavaScript 中用于处理异步操作的一种对象,它代表了一个异步操作的最终完成(或失败)及其结果值。Promise提供了一种更清晰、更易于管理的方式来进行异步编程,避免了“回调地狱”的问题,并且与现代 JavaScript 的async/await语法无缝集成。
其他大型语言一般是同步的,js 通过es6 promise 解决了 js是单线程,异步不好控制的问题。
Promise 的基本概念
每个 Promise 对象都处于以下三种状态之一:
- Pending(等待中): 初始状态,既不是成功也不是失败。
- Fulfilled(已兑现): 操作成功完成。
- Rejected(已拒绝): 操作失败。
一旦 Promise 进入 Fulfilled 或 Rejected 状态,它的状态就不能再改变,这保证了 Promise 的结果是不可变的。
创建 Promise
Promise是js的一个类。作为类,它提供了一个构造函数,用于创建新的 Promise 实例。你可以通过 new Promise() 构造函数创建一个新的 Promise 实例,并传入一个执行器函数(executor) 作为参数。这个执行器函数接收两个参数:resolve 和 reject,它们都是函数。当异步操作成功时调用 resolve,失败时调用 reject。
使用 .then() 方法来处理 Promise 成功的结果,使用 .catch() 方法来处理失败的情况。.then() 方法接受两个可选的回调函数作为参数:第一个处理成功的回调,第二个处理失败的回调;而 .catch() 只接受一个处理错误的回调。
// 实例化时,传递函数,里面装耗时性任务
const p = new Promise((resolve,reject)=>{ // executor 执行器
console.log('333'); // 同步任务
setTimeout(()=>{ // 异步任务 放入 event loop
console.log('222');
// resolve(); //何时运行
// reject();
resolve('325');
},1000)
})
// 异步任务的执行顺序控制的话 用promise
console.log(p.__proto__,p);
p
.then((data)=>{
// 等到executor 异步任务执行完毕后,在执行then里面的函数
console.log('111');
console.log(p);
console.log(data);
})
.catch(()=>{
// 如果在 executor 中使用了 reject 将会报错并捕获
console.log('error');
})
执行顺序如下
手写AJAX
创建函数,返回Promise对象
执行器函数中提供两个参数resolve,reject。
// url -> http(200+4) -> 异步耗时任务 -> 执行流程(DOM) -> promise
const getJSON = function(url){
// 返回promise
return new Promise((resolve,reject)=>{ // executor 执行器 立即执行
})
}
getJSON('https://api.github.com/orgs/lemoncode/members')
.then(data=>{
console.log(JSON.parse(data));
})
创建 XMLHttpRequest 对象
如果当前环境支持 XMLHttpRequest(现代浏览器),则创建一个新的 XMLHttpRequest 实例;否则(针对非常旧的IE浏览器,目前已经几乎见不到了),创建一个 ActiveXObject 实例。这是为了确保代码在不同浏览器环境中都能正常工作。
const xhr = XMLHttpRequest // 浏览器嗅探 IE 早期不支持
? new XMLHttpRequest()
// 激活
: new ActiveXObject("Microsoft.XMLHTTP"); // 微软推出,核心对象
配置请求
使用XMLHttpRequest中的open方法,接收三个参数:请求方法、请求的url、是否异步。
// 请求方法 请求的url 是否异步
xhr.open('GET',url,true) // 配置请求
注册事件处理函数,监听
当请求完成并且服务器返回响应后,我们可以监听XMLHttpRequest对象上的事件来处理结果。最常用的是onreadystatechange事件,它会在readyState属性改变时触发。readyState有五个可能的值,表示请求的不同状态:
0: 请求未初始化。1: 服务器连接已建立。2: 请求已接收。3: 请求处理中。4: 请求已完成,且响应已就绪。
我们通常对readyState为4的情况感兴趣,这意味着请求已完成,并且可以通过检查status属性来确认请求是否成功(例如,status为200表示成功)。然后我们可以从responseText属性读取服务器的响应文本,或者如果是JSON格式的数据,可以从responseJSON属性读取(需确保服务器端正确设置了Content-Type: application/json响应头)。
检查 status 属性来确定请求是否成功。200 表示请求成功,304 表示资源未修改(客户端可以从缓存中获取数据)。这两种情况都视为成功,调用 resolve 并传入响应文本。如果状态码不是 200 或 304,则认为请求失败,调用 reject 并传入一个包含错误信息的新 Error 对象。
// 注册事件处理函数,当请求状态改变时触发
xhr.onreadystatechange = function(){
if(xhr.readyState !== 4) return // 是否成功到达
// 第一次 查找 200 后端开销
// 之后来, 只要后端数据没有发生改变,没有必要再去查,
// 304 ,不传内容
// 告诉浏览器,直接使用本地数据
if(xhr.status === 200 || xhr.status === 304){ // 200 成功 304 Not Modified 缓存
resolve(xhr.responseText) // 成功
}else{
reject(new Error(xhr.responseText)) // 失败
}
}
发送请求
xhr.send(); // 发送请求
完整代码
// url -> http(200+4) -> 异步耗时任务 -> 执行流程(DOM) -> promise
const getJSON = function(url){
// 返回 promise
return new Promise((resolve,reject)=>{ // executor 执行器 立即执行
const xhr = XMLHttpRequest // 浏览器嗅探 IE 早期不支持
? new XMLHttpRequest()
// 激活
: new ActiveXObject("Microsoft.XMLHTTP"); // 微软推出,核心对象
// 第三个参数 是否异步
xhr.open('GET',url,true) // 配置请求
// 注册事件处理函数,当请求状态改变时触发
xhr.onreadystatechange = function(){
if(xhr.readyState !== 4) return // 是否成功到达
// 第一次 查找 200 后端开销
// 之后来, 只要后端数据没有发生改变,没有必要再去查,
// 304 ,不传内容
// 告诉浏览器,直接使用本地数据
if(xhr.status === 200 || xhr.status === 304){ // 200 成功 304 Not Modified 缓存
resolve(xhr.responseText) // 成功
}else{
reject(new Error(xhr.responseText)) // 失败
}
}
xhr.send(); // 发送请求
})
}
getJSON('https://api.github.com/orgs/lemoncode/members')
.then(data=>{
console.log(JSON.parse(data));
})
结语
以上就是关于手写ajax的基本介绍。
通过本文,我们深入探讨了如何使用 Promise 封装 XMLHttpRequest 来手写 ajax 请求。我们创建了一个名为 getJSON 的函数,该函数返回一个新的 Promise 实例,用于封装与服务器通信的异步请求逻辑。在实现过程中,我们不仅处理了不同浏览器环境下的兼容性问题,还确保了对请求状态和 HTTP 状态码的精确判断,从而优雅地处理成功响应和错误情况。这使得我们可以更灵活、更可靠地获取远程数据,并将其集成到前端应用中。
希望这篇文章能给你带来帮助。都看到这了,麻烦点个赞呗。