基于 Promise 的 AJAX 封装实现

186 阅读4分钟

基于 Promise 的 AJAX 封装实现

mountain-7011121_1280.webp

在众多数据请求方式中,ajax 作为现代 JavaScript 的重要组成部分,为开发者提供了一种简洁且强大的网络请求方式。在开始手写 ajax-promise 之前,我们先来了解一些重要的基础概念。

一、接口地址与网站地址

  1. 接口地址:即应用程序编程接口(API),是后端开发人员为开发者提供的一组用于访问、操作或获取数据的规则、协议和路径。接口地址通常是一个 URL。通过这个地址,开发者可以向服务器发送请求,请求获取特定的数据或执行特定的操作。

    例如,https://api.github.com/users/xxx 就是一个典型的接口地址,开发者可以通过它获取 GitHub 用户的相关信息。接口地址十分灵活,能够包含查询参数、路径参数、请求体等丰富信息,以精准指定请求的具体内容和操作。

  2. 网站地址:面向普通用户的入口。用户在浏览器中输入网站地址,如 www.baidu.com ,就能访问网站的主页或特定页面。

    • 网站地址同样基于 URL
    • 它的主要作用是为用户展示网页内容,虽然也能包含查询参数、路径参数等信息。
    • 目的是为了呈现不同的页面内容,而非像接口地址那样专注于数据交互。

​ 总的来说接口地址是面向开发者的,网站地址是面向用户的。

二、ajax的背后是什么

要理解ajax的工作原理,我们需要先对xhr技术有一定的了解。XMLHttpRequest (XHR) 是浏览器提供的用于在 JavaScript 中发起 HTTP 请求的 API,它是 AJAX (Asynchronous JavaScript and XML) 技术的核心,XHR 基础用法如下:

  1. 创建XHR对象

    const xhr = new XMLHttpRequest();
    
  2. 配置请求

    xhr.open('GET', 'https://api.example.com/data', true); // 异步请求
    
  3. 设置回调函数

    xhr.onload = function() {
      if (xhr.status >= 200 && xhr.status < 300) {
        console.log('成功:', xhr.responseText);
      } else {
        console.error('请求失败:', xhr.statusText);
      }
    };
    xhr.onerror = function() {
      console.error('请求出错');
    };
    
  4. 发送请求

    xhr.send() 
    

xhr不同的是fetch 函数的返回一个 Promise 实例化对象。当 Promise 处于 pending 状态时,表示异步操作正在进行;一旦操作成功完成,Promise 会变为 fulfilled 状态,并可以通过 then 方法获取成功的结果;而如果操作失败,Promise 则会进入 rejected 状态,此时可以通过 catch 方法捕获错误信息。这种状态管理机制使得异步操作的代码更加清晰、易读,避免了 “回调地狱” 的问题。

三、手写 ajax-promise的核心步骤与实现

这里我们以xhr封装来实现浏览器请求

  1. 创建 XMLHttpRequest 对象XMLHttpRequest 是浏览器提供的用于进行 HTTP 请求的对象,它是实现网络请求的基础。通过 new XMLHttpRequest() 即可创建一个新的实例。

    const myAjax = async function (url) {
      return new Promise((resolve, reject) => {
         const xhr = new XMLHttpRequest();
      });
    };
    
    
  2. 打开请求:使用 open 方法指定请求的类型(如 GETPOST 等)和请求的 URL。同时,还可以设置是否为异步请求,通常情况下我们希望请求是异步的,这样不会阻塞主线程。

    xhr.open("GET", url);
    
  3. 发送请求:根据请求的类型,准备好请求体数据(如果是 POST 等需要请求体的类型),然后使用 send 方法发送请求。

    xhr.send();
    
  4. 监听请求状态:通过监听 xhr 对象的 onreadystatechange 事件,获取请求的状态变化。当 xhr.readyState4 时,表示请求已完成,此时可以进一步判断请求的状态码。

    请求状态码:

    • 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);
        });

效果如下所示:

![image-20250629220333876.png](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/e2a35b0fce8b4b8ebabeee9a7ae026dc~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgR-etieS9oOS4i-ivvg==:q75.awebp?rk3s=f64ab15b&x-expires=1771161223&x-signature=GFJe6MNbSZu7KinNHEH3wyssR1c%3D)