基于 Promise 的 AJAX 封装实现
在众多数据请求方式中,ajax 作为现代 JavaScript 的重要组成部分,为开发者提供了一种简洁且强大的网络请求方式。在开始手写 ajax-promise 之前,我们先来了解一些重要的基础概念。
一、接口地址与网站地址
-
接口地址:即应用程序编程接口(API),是后端开发人员为开发者提供的一组用于访问、操作或获取数据的规则、协议和路径。接口地址通常是一个 URL。通过这个地址,开发者可以向服务器发送请求,请求获取特定的数据或执行特定的操作。
例如,
https://api.github.com/users/xxx就是一个典型的接口地址,开发者可以通过它获取 GitHub 用户的相关信息。接口地址十分灵活,能够包含查询参数、路径参数、请求体等丰富信息,以精准指定请求的具体内容和操作。 -
网站地址:面向普通用户的入口。用户在浏览器中输入网站地址,如
www.baidu.com,就能访问网站的主页或特定页面。- 网站地址同样基于 URL
- 它的主要作用是为用户展示网页内容,虽然也能包含查询参数、路径参数等信息。
- 目的是为了呈现不同的页面内容,而非像接口地址那样专注于数据交互。
总的来说接口地址是面向开发者的,网站地址是面向用户的。
二、ajax的背后是什么
要理解ajax的工作原理,我们需要先对xhr技术有一定的了解。XMLHttpRequest (XHR) 是浏览器提供的用于在 JavaScript 中发起 HTTP 请求的 API,它是 AJAX (Asynchronous JavaScript and XML) 技术的核心,XHR 基础用法如下:
-
创建XHR对象
const xhr = new XMLHttpRequest(); -
配置请求
xhr.open('GET', 'https://api.example.com/data', true); // 异步请求 -
设置回调函数
xhr.onload = function() { if (xhr.status >= 200 && xhr.status < 300) { console.log('成功:', xhr.responseText); } else { console.error('请求失败:', xhr.statusText); } }; xhr.onerror = function() { console.error('请求出错'); }; -
发送请求
xhr.send()
与xhr不同的是fetch 函数的返回一个 Promise 实例化对象。当 Promise 处于 pending 状态时,表示异步操作正在进行;一旦操作成功完成,Promise 会变为 fulfilled 状态,并可以通过 then 方法获取成功的结果;而如果操作失败,Promise 则会进入 rejected 状态,此时可以通过 catch 方法捕获错误信息。这种状态管理机制使得异步操作的代码更加清晰、易读,避免了 “回调地狱” 的问题。
三、手写 ajax-promise的核心步骤与实现
这里我们以xhr封装来实现浏览器请求
-
创建
XMLHttpRequest对象:XMLHttpRequest是浏览器提供的用于进行 HTTP 请求的对象,它是实现网络请求的基础。通过new XMLHttpRequest()即可创建一个新的实例。const myAjax = async function (url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); }); }; -
打开请求:使用
open方法指定请求的类型(如GET、POST等)和请求的 URL。同时,还可以设置是否为异步请求,通常情况下我们希望请求是异步的,这样不会阻塞主线程。xhr.open("GET", url); -
发送请求:根据请求的类型,准备好请求体数据(如果是
POST等需要请求体的类型),然后使用send方法发送请求。xhr.send(); -
监听请求状态:通过监听
xhr对象的onreadystatechange事件,获取请求的状态变化。当xhr.readyState为4时,表示请求已完成,此时可以进一步判断请求的状态码。请求状态码:
- 1 (OPENED):已调用 open() 方法,建立了与服务器的连接,但尚未发送请求。此时可以设置请求头或准备发送数据。
- 2(HEADERS_RECEIVED) :服务器已接收请求并响应了状态行和响应头,但响应体尚未开始接收。可通过
getResponseHeader()获取响应头信息。 - 3(LOADING):服务器正在传输响应体数据,
responseText属性已包含部分数据。此状态可能会多次触发(取决于数据传输分块)。 - 4(DONE):请求完成,响应数据已全部接收(接口响应的内容到达了)。此时可通过 status 属性判断请求成功与否(如200表示成功),并处理
responseText或responseXML。
xhr.onreadystatechange = function () { if (xhr.readyState === 4) { // 成功 if (xhr.status === 200) { // 调用resolve表示Promis异步任务执行完成 // resolve 会将异步任务的结果返回 resolve(JSON.parse(xhr.responseText)); } else { // reject 会将异步任务执行失败的原因返回 reject(JSON.parse(xhr.responseText)); } } };
通过以上步骤,我们就实现了一个简单的类似 fetch 的函数。使用时,只需像调用原生 fetch 一样传入 URL 和请求选项即可:
// 示例调用
getJson("https://api.github.com/users/**/repos")
.then((repos) => {
const reposList = document.getElementById("repos");
repos.forEach((repo) => {
const li = document.createElement("li");
li.textContent = repo.name;
reposList.appendChild(li);
});
})
.catch((error) => {
console.error("请求出错:", error);
});
效果如下所示:
