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));
相关推荐
大菠萝学姐2 小时前
基于springboot的旅游攻略网站设计与实现
前端·javascript·vue.js·spring boot·后端·spring·旅游
心随雨下2 小时前
TypeScript中extends与implements的区别
前端·javascript·typescript
摇滚侠2 小时前
Vue 项目实战《尚医通》,底部组件拆分与静态搭建,笔记05
前端·vue.js·笔记·vue
双向332 小时前
CANN训练营实战指南:从算子分析到核函数定义的完整开发流程
前端
caleb_5202 小时前
vue cli的介绍
前端·javascript·vue.js
Swift社区2 小时前
如何监测 Vue + GeoScene 项目中浏览器内存变化并优化性能
前端·javascript·vue.js
WYiQIU2 小时前
大厂前端岗重复率极高的场景面试原题解析
前端·javascript·vue.js·react.js·面试·状态模式
IT_陈寒2 小时前
Redis 高并发实战:我从 5000QPS 优化到 5W+ 的7个核心策略
前端·人工智能·后端
vortex53 小时前
ASP vs ASP.NET vs ASP.NET Core:三代微软 Web 技术核心区别解析
前端·microsoft·asp.net