使用 Promise 控制 AJAX 请求顺序

在前端开发中,AJAX 请求是一种常见的异步操作,用于在不刷新页面的情况下与服务器交互。然而,当多个 AJAX 请求需要按照特定顺序执行时,传统的回调嵌套方式会让代码变得难以维护和理解。幸运的是,Promise 提供了一种更优雅的方式来控制 AJAX 请求的顺序。本文将通过具体的代码示例,展示如何使用 Promise 来实现 AJAX 请求的顺序执行。

一、Promise 简介

Promise 是一个表示异步操作最终完成或失败的对象。它有三种状态:

  1. Pending(进行中):初始状态,既不是成功,也不是失败。

  2. Fulfilled(已成功):操作成功完成。

  3. Rejected(已失败):操作失败。

Promise 的核心方法是 then(),用于处理异步操作的成功和失败情况。此外,catch() 方法用于捕获错误,finally() 方法用于执行异步操作完成后的清理工作。

二、使用 Promise 控制 AJAX 请求顺序

(一)场景描述

假设我们需要依次执行多个 AJAX 请求,每个请求的结果可能会影响下一个请求的参数。例如:

  1. 第一步:获取用户信息。

  2. 第二步:根据用户信息获取订单列表。

  3. 第三步:根据订单列表获取订单详情。

(二)代码示例

1. 创建 AJAX 请求函数

首先,我们创建一个通用的 AJAX 请求函数,返回一个 Promise 对象。

function ajax(url, data = {}) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('POST', url, true);
    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.onreadystatechange = () => {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          resolve(JSON.parse(xhr.responseText));
        } else {
          reject(new Error(`Request failed with status: ${xhr.status}`));
        }
      }
    };
    xhr.send(JSON.stringify(data));
  });
}

2. 依次执行 AJAX 请求

接下来,我们使用 Promise 的链式调用来依次执行多个 AJAX 请求。

// 第一步:获取用户信息
function getUserInfo() {
  return ajax('https://api.example.com/user');
}

// 第二步:根据用户信息获取订单列表
function getOrderList(userId) {
  return ajax('https://api.example.com/orders', { userId });
}

// 第三步:根据订单列表获取订单详情
function getOrderDetails(orderId) {
  return ajax('https://api.example.com/order', { orderId });
}

// 依次执行请求
getUserInfo()
  .then(userInfo => {
    console.log('User Info:', userInfo);
    return getOrderList(userInfo.id); // 使用用户 ID 获取订单列表
  })
  .then(orderList => {
    console.log('Order List:', orderList);
    return getOrderDetails(orderList[0].id); // 使用第一个订单 ID 获取订单详情
  })
  .then(orderDetails => {
    console.log('Order Details:', orderDetails);
  })
  .catch(error => {
    console.error('Error:', error);
  });

(三)运行结果

  1. 第一步:调用 getUserInfo() 获取用户信息。

  2. 第二步:使用用户信息中的 id 调用 getOrderList() 获取订单列表。

  3. 第三步:使用订单列表中的第一个订单 id 调用 getOrderDetails() 获取订单详情。

如果任何一个请求失败,catch 会捕获错误并打印出来,整个链式调用会中断。

三、使用 async/await 进一步简化代码

async/awaitPromise 的语法糖,可以让异步代码看起来像同步代码,进一步简化逻辑。

使用 async/await 重写代码

// 创建 AJAX 请求函数
function ajax(url, data = {}) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('POST', url, true);
    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.onreadystatechange = () => {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          resolve(JSON.parse(xhr.responseText));
        } else {
          reject(new Error(`Request failed with status: ${xhr.status}`));
        }
      }
    };
    xhr.send(JSON.stringify(data));
  });
}

// 定义异步函数
async function fetchOrderDetails() {
  try {
    // 第一步:获取用户信息
    const userInfo = await ajax('https://api.example.com/user');
    console.log('User Info:', userInfo);

    // 第二步:根据用户信息获取订单列表
    const orderList = await ajax('https://api.example.com/orders', { userId: userInfo.id });
    console.log('Order List:', orderList);

    // 第三步:根据订单列表获取订单详情
    const orderDetails = await ajax('https://api.example.com/order', { orderId: orderList[0].id });
    console.log('Order Details:', orderDetails);
  } catch (error) {
    console.error('Error:', error);
  }
}

// 调用异步函数
fetchOrderDetails();

(四)运行结果

与链式调用类似,async/await 的代码更加直观,逻辑更加清晰。每个 await 表示等待一个异步操作完成,如果操作失败,会直接抛出错误并被捕获。

四、总结

通过使用 Promise,我们可以优雅地控制 AJAX 请求的顺序,避免回调嵌套的复杂性。Promise 的链式调用和 async/await 语法糖让异步代码更加清晰易读,同时也提高了代码的可维护性。

在实际开发中,合理使用 Promiseasync/await 可以显著提升开发效率和代码质量。希望本文的示例能够帮助你更好地理解和应用这些强大的工具。

正文到此结束