AJAX和Promise

文章目录

AJAX

AJAX 是浏览器与服务器进行数据通信的技术,AJAX是异步的JavaScript和XML,就是使用XMLHttpRequest对象与服务器通信,它可以使用JSON、XML、HTML和text文本等格式发送和接收数据。AJAX最大特点是异步,可以不刷新页面的情况下与服务器通信,交换数据。

XMLHttpRequest

AJAX是基于XMLHttpRequest进行实现,以下是XMLHttpRequest的基本使用。

html 复制代码
<!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>
    <div class="myp"></div>
    <script>
        // 1.创建XMLHttpRequest对象
        const xhr = new XMLHttpRequest()
        // 2.配置请求方法和url
        xhr.open('GET', '')  
        // 3.配置监听loadend事件,接收响应结果
        xhr.addEventListener('loadend', () => {
            console.log(xhr.response);
            const data = JSON.parse(xhr.response)
            console.log(data.list.join('<br>'));
            document.querySelector('.myp').innerHTML = data.list.join('<br>')
        })
        // 4.发起请求
        xhr.send()

    </script>
</body>
</html>

Axios使用

Axios 是一个基于Promise的网络请求库,在现代前端开发中,Axios 已经成为事实上的标准,提供了更现代化、更强大的 HTTP 客户端功能。

javascript 复制代码
<!-- axios库地址CDN -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
	axios({
		url: '目标资源地址',
		method: '请求方法',
		params: { 
			参数名: 值
		}, // 查询参数
		data: {
			参数名: 值
		} // 提交数据
}).then(result => {
	console.log(result)
}).catch(error => {
	// 错误处理
})
</script>

Http请求方法

请求方法 操作
GET 获取数据
POST 数据提交
PUT 修改数据(全部)
DELETE 删除数据
PATCH 修改数据(部分)

form-serialize 插件

使用serialize函数,快速收集表单元素的值

javascript 复制代码
<script src="./lib/form-serialize.js"></script>
<script>
    document.querySelector('.btn').addEventListener('click', () => {
      /**
       * 2. 使用serialize函数,快速收集表单元素的值
       * 参数1:要获取哪个表单的数据
       *  表单元素设置name属性,值会作为对象的属性名
       *  建议name属性的值,最好和接口文档参数名一致
       * 参数2:配置对象
       *  hash 设置获取数据结构
       *    - true:JS对象(推荐)一般请求体里提交给服务器
       *    - false: 查询字符串
       *  empty 设置是否获取空值
       *    - true: 获取空值(推荐)数据结构和标签结构一致
       *    - false:不获取空值
      */
      const form = document.querySelector('.example-form')
      const data = serialize(form, { hash: true, empty: true })
      // const data = serialize(form, { hash: false, empty: true })
      // const data = serialize(form, { hash: true, empty: false })
      console.log(data)
    })
</script>

上传图片

html 复制代码
<!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>

    <input type="file" class="upload">

    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    <script>
        document.querySelector('.upload').addEventListener('click', e => {
            console.log(e.target.files[0]);

            const fd = new FormData()
            fd.append('img', e.target.files[0])
            
            axios({
                url: '',
                method: 'post',
                data: fd
            })
        })
    </script>
</body>
</html>

Promise

基本语法

Promise对象用于表示一个异步操作的最终完成或失败及其结果值。基本语法:

javascript 复制代码
// 1.创建Promise对象
const p = new Promise((resolve, reject) => {
    // 2.执行异步代码
    // 成功调用 resolve('模拟AJAX请求-成功结果')
    // 失败调用 reject(new Error('模拟AJAX请求-失败结果'))
})
// 3.获取结果
p.then(result => {
    // 成功
}).catch(error => {
    // 失败
})

Promise的基本原理

把"将来会得到的结果"封装成一个对象,并允许你现在就注册"成功/失败时要做什么"。换句话说,Promise是一个"未来值"的占位符 + 回调管理器。

以下是Promise的一个简易实现,用于理解Promise的核心设计思想。

javascript 复制代码
function SimplePromise(executor) {
  let state = 'pending'; // 'pending' | 'fulfilled' | 'rejected'
  let value;             // 成功的值
  let reason;            // 失败的原因
  let onFulfilledCallbacks = [];
  let onRejectedCallbacks = [];

  const resolve = (val) => {
    if (state !== 'pending') return;
    state = 'fulfilled';
    value = val;
    onFulfilledCallbacks.forEach(cb => cb(value));
  };

  const reject = (err) => {
    if (state !== 'pending') return;
    state = 'rejected';
    reason = err;
    onRejectedCallbacks.forEach(cb => cb(reason));
  };

  try {
    executor(resolve, reject);
  } catch (e) {
    reject(e);
  }

  this.then = function(onFulfilled, onRejected) {
    return new SimplePromise((resolveNext, rejectNext) => {
      // 成功处理函数
      const handleFulfilled = (val) => {
        try {
          const result = onFulfilled(val);
          resolveNext(result);
        } catch (e) {
          rejectNext(e);
        }
      };

      // 失败处理函数
      const handleRejected = (err) => {
        try {
          const result = onRejected ? onRejected(err) : err;
          // 如果用户没传 onRejected,就继续抛出错误(错误冒泡)
          if (onRejected) {
            resolveNext(result); // 注意:catch 后返回的是成功值!
          } else {
            rejectNext(result);
          }
        } catch (e) {
          rejectNext(e);
        }
      };

      if (state === 'fulfilled') {
        handleFulfilled(value);
      } else if (state === 'rejected') {
        handleRejected(reason);
      } else {
        onFulfilledCallbacks.push(handleFulfilled);
        onRejectedCallbacks.push(handleRejected);
      }
    });
  };
}

async / await 是 JavaScript 中用于处理异步操作的语法糖,底层完全基于 Promise,await 本质上是 .then() 的语法糖,而 async 函数总是返回一个 Promise。

javascript 复制代码
async function fetchData() {
  const res = await fetch('/api/data'); // 等待 Promise 完成
  const data = await res.json();
  return data; // 自动包装成 Promise
}

// 调用
fetchData().then(data => console.log(data));

等价于

javascript 复制代码
function fetchData() {
  return fetch('/api/data')
    .then(res => res.json())
    .then(data => data); // 返回值自动被 Promise.resolve 包装
}

回调地狱问题

在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱,可读性差,异常无法获取,耦合性严重,牵一发动全身

javascript 复制代码
 /**
     * 需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中
    */
    // 1. 获取默认第一个省份的名字
    axios({url: 'http://xxx/api/province'}).then(result => {
      const pname = result.data.list[0]
      document.querySelector('.province').innerHTML = pname
      // 2. 获取默认第一个城市的名字
      axios({url: 'http://xxx/api/city', params: { pname }}).then(result => {
        const cname = result.data.list[0]
        document.querySelector('.city').innerHTML = cname
        // 3. 获取默认第一个地区的名字
        axios({url: 'http://xxx/api/area', params: { pname, cname }}).then(result => {
          console.log(result)
          const areaName = result.data.list[0]
          document.querySelector('.area').innerHTML = areaName
        })
      })
    }).catch(error => {
      console.dir(error)
    })

使用Promise链式调用进行优化,可以使代码更加清晰。

javascript 复制代码
    let pname = ''
    // 1. 得到-获取省份Promise对象
    axios({url: 'http://hmajax.itheima.net/api/province'}).then(result => {
      pname = result.data.list[0]
      document.querySelector('.province').innerHTML = pname
      // 2. 得到-获取城市Promise对象
      return axios({url: 'http://hmajax.itheima.net/api/city', params: { pname }})
    }).then(result => {
      const cname = result.data.list[0]
      document.querySelector('.city').innerHTML = cname
      // 3. 得到-获取地区Promise对象
      return axios({url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }})
    }).then(result => {
      console.log(result)
      const areaName = result.data.list[0]
      document.querySelector('.area').innerHTML = areaName
    })

使用async和await语法,解决回调函数地狱

在async函数内,使用await关键字,获取Promise对象"成功状态"结果值

注意:await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)

javascript 复制代码
    // 1. 定义async修饰函数
    async function getData() {
      // 2. await等待Promise对象成功的结果
      const pObj = await axios({url: 'http://hmajax.itheima.net/api/province'})
      const pname = pObj.data.list[0]
      const cObj = await axios({url: 'http://hmajax.itheima.net/api/city', params: { pname }})
      const cname = cObj.data.list[0]
      const aObj = await axios({url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }})
      const areaName = aObj.data.list[0]


      document.querySelector('.province').innerHTML = pname
      document.querySelector('.city').innerHTML = cname
      document.querySelector('.area').innerHTML = areaName
    }

    getData()

封装简易Axios

使用Promise+XMLHttpRequest封装简易的axios

javascript 复制代码
function myAxios(config) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()
        if(config.params) {
            const paramObj = new URLSearchParams(config.params)
            const queryStr = paramObj.toString()
            config.url += `?${queryStr}`
        }
        xhr.open(config.method || 'GET', config.url)
        xhr.addEventListener('loadend', () => {
           if (xhr.status >= 200 && xhr.status < 300) {
            resolve(JSON.parse(xhr.response))
           } else {
            reject(new Error(xhr.response))
           }
        })
        if(config.data) {
            xhr.setRequestHeader('Content-Type', 'application/json')
            xhr.send(JSON.stringify(data))
        } else {
            xhr.send()
        }
    })            
}
myAxios({
    url: ''
}).then(result => {
    console.log(result);
}).catch(err => {
    console.log(err);
})

事件循环-EventLoop

ES6 之后引入了 Promise 对象, 让 JS 引擎也可以发起异步任务

异步任务分为:

  • 宏任务:由浏览器环境执行的异步代码
  • 微任务:由 JS 引擎环境执行的异步代码,Promise对象.then()中的代码为微任务

Promise静态方法

Promise.resolve()

创建一个立即解析的 Promise

javascript 复制代码
// 使用值解析
Promise.resolve("成功").then(value => {
  console.log(value); // 输出: "成功"
});

// 解析一个 Promise
const original = Promise.resolve("原始值");
const wrapped = Promise.resolve(original);
wrapped.then(value => {
  console.log(value); // 输出: "原始值"
});

// 解析 thenable 对象
Promise.resolve({
  then(resolve, reject) {
    resolve("thenable 对象");
  }
}).then(value => {
  console.log(value); // 输出: "thenable 对象"
});

Promise.reject()

创建一个立即拒绝的 Promise

javascript 复制代码
Promise.reject(new Error("操作失败"))
  .catch(error => {
    console.error(error.message); // 输出: "操作失败"
  });

// 在 async 函数中使用
async function example() {
  try {
    await Promise.reject("拒绝原因");
  } catch (error) {
    console.log(error); // 输出: "拒绝原因"
  }
}
example();

Promise.all()

等待所有 Promise 完成,或第一个 Promise 被拒绝。

javascript 复制代码
// 所有 Promise 都成功
const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);

Promise.all([promise1, promise2, promise3])
  .then(values => {
    console.log(values); // 输出: [1, 2, 3]
  });

// 其中一个 Promise 失败
const successPromise = Promise.resolve("成功");
const failurePromise = Promise.reject(new Error("失败"));

Promise.all([successPromise, failurePromise])
  .then(values => {
    console.log(values); // 不会执行
  })
  .catch(error => {
    console.error(error.message); // 输出: "失败"
  });

// 实际应用:并行请求多个 API
const fetchUser = Promise.resolve({ name: "张三" });
const fetchPosts = Promise.resolve([{ id: 1, title: "文章1" }]);
const fetchComments = Promise.resolve([{ id: 1, content: "评论1" }]);

Promise.all([fetchUser, fetchPosts, fetchComments])
  .then(([user, posts, comments]) => {
    console.log("用户:", user);
    console.log("文章:", posts);
    console.log("评论:", comments);
  });

Promise.allSettled()

等待所有 Promise 完成(无论成功或失败)

javascript 复制代码
const resolvedPromise = Promise.resolve("成功");
const rejectedPromise = Promise.reject("失败");

Promise.allSettled([resolvedPromise, rejectedPromise])
  .then(results => {
    results.forEach((result, index) => {
      if (result.status === "fulfilled") {
        console.log(`Promise ${index}:`, result.value);
      } else {
        console.log(`Promise ${index}:`, result.reason);
      }
    });
    // 输出:
    // Promise 0: 成功
    // Promise 1: 失败
  });

Promise.race()

返回第一个完成(成功或失败)的 Promise

javascript 复制代码
// 第一个 Promise 先完成
const fastPromise = new Promise(resolve => {
  setTimeout(() => resolve("快的"), 100);
});

const slowPromise = new Promise(resolve => {
  setTimeout(() => resolve("慢的"), 500);
});

Promise.race([fastPromise, slowPromise])
  .then(result => {
    console.log(result); // 输出: "快的"
  });

// 超时控制
function timeout(ms) {
  return new Promise((_, reject) => {
    setTimeout(() => reject(new Error("超时")), ms);
  });
}

function fetchWithTimeout(url, timeoutMs) {
  return Promise.race([
    fetch(url),
    timeout(timeoutMs)
  ]);
}

// 模拟使用
const mockFetch = Promise.resolve("数据获取成功");
fetchWithTimeout(mockFetch, 2000)
  .then(data => console.log(data))
  .catch(error => console.error(error.message));

Promise.any()

返回第一个成功的 Promise,如果所有 Promise 都失败则抛出 AggregateError

javascript 复制代码
// 第一个成功的 Promise
const failure1 = Promise.reject("错误1");
const failure2 = Promise.reject("错误2");
const success = Promise.resolve("成功");

Promise.any([failure1, failure2, success])
  .then(result => {
    console.log(result); // 输出: "成功"
  });

// 所有 Promise 都失败
const allFailures = [
  Promise.reject("错误A"),
  Promise.reject("错误B"),
  Promise.reject("错误C")
];

Promise.any(allFailures)
  .catch(error => {
    console.log(error instanceof AggregateError); // true
    console.log(error.errors); // ["错误A", "错误B", "错误C"]
  });

Promise.try() (非标准,但常用)

立即执行异步函数,同步错误也会被捕获

javascript 复制代码
// 模拟实现(非原生方法)
Promise.try = function(fn) {
  return new Promise(resolve => resolve(fn()));
};

// 使用示例
function asyncOperation() {
  // 可能抛出同步错误
  if (Math.random() > 0.5) {
    throw new Error("同步错误");
  }
  return "操作成功";
}

// 传统方式需要 try-catch
try {
  asyncOperation().then(console.log);
} catch (error) {
  console.error("捕获错误:", error.message);
}

// 使用 Promise.try
Promise.try(asyncOperation)
  .then(result => console.log(result))
  .catch(error => console.error("捕获错误:", error.message));
相关推荐
崔庆才丨静觅8 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了9 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅9 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅9 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅10 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊10 小时前
jwt介绍
前端
爱敲代码的小鱼10 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax