前端面试高频考点——手写AJAX

619 阅读6分钟

引言

手写ajax一直是前端面试中的高频考点。本篇文章将带你使用Promise封装ajax请求。本篇会涉及到fetchPromise的一些基础,这也能帮助我们更进一步理解实现ajax的过程。

4655646.jpg

fetch

在手写ajax之前,我们先来了解我们要实现的fetch

fetch 是一种现代的、更强大的用于发起网络请求的API,它提供了比传统的 XMLHttpRequest 更简洁和灵活的接口。fetch 返回一个 Promise,这使得它更容易处理异步操作,并且与 async/await 语法无缝集成。

我们在本篇文章中访问的地址,这是github开源项目中团队 "lemoncode" 的成员列表 api.github.com/orgs/lemonc…

这是网站地址中的JSON结构

image.png

下面是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>

image.png

Promise

fetch的实现基于了es6的Promise,它返回了一个Promise对象。为了实现手写ajax,我们还需要了解Promise

Promise 是 JavaScript 中用于处理异步操作的一种对象,它代表了一个异步操作的最终完成(或失败)及其结果值。Promise 提供了一种更清晰、更易于管理的方式来进行异步编程,避免了“回调地狱”的问题,并且与现代 JavaScript 的 async/await 语法无缝集成。

其他大型语言一般是同步的,js 通过es6 promise 解决了 js是单线程,异步不好控制的问题。

Promise 的基本概念

每个 Promise 对象都处于以下三种状态之一:

  • Pending(等待中): 初始状态,既不是成功也不是失败。
  • Fulfilled(已兑现): 操作成功完成。
  • Rejected(已拒绝): 操作失败。

一旦 Promise 进入 FulfilledRejected 状态,它的状态就不能再改变,这保证了 Promise 的结果是不可变的。

创建 Promise

Promise是js的一个类。作为类,它提供了一个构造函数,用于创建新的 Promise 实例。你可以通过 new Promise() 构造函数创建一个新的 Promise 实例,并传入一个执行器函数(executor) 作为参数。这个执行器函数接收两个参数:resolvereject,它们都是函数。当异步操作成功时调用 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');
})

执行顺序如下

image.png

手写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: 请求已完成,且响应已就绪。

我们通常对readyState4的情况感兴趣,这意味着请求已完成,并且可以通过检查status属性来确认请求是否成功(例如,status200表示成功)。然后我们可以从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 状态码的精确判断,从而优雅地处理成功响应和错误情况。这使得我们可以更灵活、更可靠地获取远程数据,并将其集成到前端应用中。

希望这篇文章能给你带来帮助。都看到这了,麻烦点个赞呗。

121112.jpg