手把手教你封装一个getJSON函数:从AJAX到Promise的优雅转型

46 阅读4分钟

在前端开发中,异步请求是必不可少的环节。随着ES6的普及,Promise已经成为处理异步操作的主流方式。今天,我将带你从基础开始,手把手封装一个getJSON函数,让你轻松掌握Promise的使用,告别回调地狱!

一、为什么我们需要getJSON函数?

在JavaScript中,我们经常需要通过网络请求获取数据。传统的AJAX方式需要处理回调函数,代码容易变得复杂和难以维护。而现代的fetch API虽然简单,但并不是所有项目都能使用。因此,封装一个支持Promise的getJSON函数,既能保持代码简洁,又能兼容更多环境。

AJAX vs Fetch

特性AJAXFetch
实现方式回调函数Promise
代码复杂度高(回调地狱)低(链式调用)
使用难度较难简单
现代支持旧式现代浏览器支持
// AJAX方式(回调地狱示例)
$.ajax({
  url: 'https://api.github.com/users/shunwuyu',
  success: function(data) {
    console.log(data);
    // 如果需要后续操作,又要嵌套一层
    $.ajax({
      url: data.repos_url,
      success: function(repos) {
        console.log(repos);
      }
    });
  }
});

// Fetch方式(Promise链式调用)
fetch('https://api.github.com/users/shunwuyu')
  .then(res => res.json())
  .then(data => console.log(data))
  .catch(error => console.error('请求出错:', error));

二、Promise:异步编程的革命

Promise是ES6引入的异步编程解决方案,它让异步代码变得更清晰、更容易维护。

Promise基础概念

  • Promise类:为异步变同步而设计的流程控制工具
  • 状态pending(等待)→ fulfilled(成功)或 rejected(失败)
  • 方法then(成功处理)、catch(失败处理)、finally(无论成功失败都会执行)
const promise = new Promise((resolve, reject) => {
  // 异步操作
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve('操作成功');
    } else {
      reject('操作失败');
    }
  }, 1000);
});

promise
  .then(result => console.log(result)) // 操作成功
  .catch(error => console.error(error))
  .finally(() => console.log('操作完成'));

三、手写getJSON函数

现在,让我们手写一个支持Promise的getJSON函数,它将AJAX请求封装为Promise风格:

const getJson = (url) => 
  new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.send();
    
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4 && xhr.status === 200) {
        resolve(JSON.parse(xhr.responseText));
      }
    };
    
    xhr.onerror = function() {
      reject('请求出错');
    };
  });

代码解析

  1. new Promise:创建一个新的Promise实例

  2. xhr.open('GET', url, true) :初始化请求,true表示异步请求

  3. xhr.send() :发送请求

  4. onreadystatechange:当请求状态改变时触发

    • readyState === 4:请求完成
    • status === 200:HTTP状态码为成功
  5. resolve(JSON.parse(xhr.responseText)) :成功时解析JSON并传递数据

  6. reject('请求出错') :失败时触发错误

四、使用getJSON获取GitHub用户数据

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>使用getJSON获取GitHub用户数据</title>
</head>
<body>
  <script>
    // 封装的getJSON函数
    const getJson = (url) => 
      new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);
        xhr.send();
        
        xhr.onreadystatechange = function() {
          if (xhr.readyState === 4 && xhr.status === 200) {
            resolve(JSON.parse(xhr.responseText));
          }
        };
        
        xhr.onerror = function() {
          reject('请求出错');
        };
      });

    // 使用getJSON获取GitHub用户数据
    getJson('https://api.github.com/users/shunwuyu')
      .then(data => {
        console.log('用户信息:', data);
        console.log('用户名:', data.login);
        console.log('GitHub地址:', data.html_url);
      })
      .catch(error => console.error('获取用户信息失败:', error));
  </script>
</body>
</html>

五、Promise的高级用法:手写sleep函数

Promise不仅用于网络请求,还可以用于各种异步操作,比如模拟等待:

// 简洁版sleep函数
const sleep = (n) => new Promise(resolve => setTimeout(() => resolve('休息完成'), n));

// 使用sleep
sleep(3000)
  .then(message => console.log(message))
  .catch(error => console.error('出错了:', error))
  .finally(() => console.log('操作完成'));

六、引用式拷贝:理解JavaScript内存机制

在前端开发中,我们经常需要复制对象。JavaScript的变量分为栈内存堆内存

  • 栈内存:存储简单数据类型(数字、字符串、布尔值等)
  • 堆内存:存储复杂数据类型(对象、数组等)
// 引用式拷贝示例
const arr = [1, 2, 3, 4, 5];
const arr2 = arr; // 引用式拷贝,指向同一块内存

arr2[0] = 10;
console.log(arr); // [10, 2, 3, 4, 5],原始数组也被修改了

// 深拷贝(创建新对象)
const arr3 = JSON.parse(JSON.stringify(arr)); // 深拷贝
arr3[0] = 0;
console.log(arr); // [10, 2, 3, 4, 5],原始数组未被修改
console.log(arr3); // [0, 2, 3, 4, 5]

七、总结

  1. getJSON函数:将AJAX封装为Promise风格,让异步代码更简洁、易读
  2. Promise:异步编程的最佳实践,避免回调地狱
  3. 引用式拷贝:理解JavaScript内存机制,避免意外修改数据

通过本文,你应该已经掌握了如何封装一个getJSON函数,并理解了Promise的核心概念。在实际项目中,你可以将这个函数封装到工具库中,方便后续使用。

小提示:现代浏览器已经支持fetch,如果你的项目环境支持,直接使用fetch会更简单。但理解getJSON的实现原理,能帮助你更好地掌握异步编程的核心思想。

希望这篇文章能帮助你提升JavaScript异步编程的能力!如果你有任何问题,欢迎在评论区讨论。