当前位置: 首页 > news >正文

前端异步编程基础

异步编程基础

本文主要介绍回调函数,Promise,aysnc/wait这三种异步机制,顺便说说axios,还有不要和我一样弄混ajax与异步的关系,正常走学校教学路线的人,可能第一个接触的就是ajax,刚好又不懂,所以可能和我一样弄混。除上述知识点,其他都是凑字数的。

注意AJAX不是前端异步的代名词

反正我学的时候老是容易搞混
请添加图片描述

混淆原因

早期 “AJAX” 一词被广泛用于指代 “异步网络请求”,因为在 AJAX 诞生初期(2005 年左右),它是前端实现异步通信的主要方式,因此很多人将 “异步” 与 “AJAX” 直接关联,AJAX 只是异步编程在 “浏览器与服务器通信” 场景下的应用

前端异步机制分类:通用机制与应用场景

一、异步编程通用机制(按实现方式分类)
机制类型核心原理典型实现优缺点
回调函数通过函数参数传递异步结果,异步任务完成后调用回调函数setTimeoutXMLHttpRequest 旧版- 简单直接
- 缺点:回调地狱、代码可读性差
Promise用状态机管理异步操作(pending/fulfilled/rejected),支持链式调用fetch()Promise.resolve()- 避免回调嵌套
- 统一错误处理(.catch()
- 代码更清晰
async/awaitPromise 的语法糖,以同步写法处理异步,自动返回 Promiseasync function + await- 最接近同步代码的写法
- 错误处理用 try...catch,更符合直觉
Generator通过迭代器(Iterator)分段执行异步任务,需手动调用 next() 推进流程function* gen() { yield ... }- 控制粒度更细
- 缺点:需手动管理流程,现代开发中较少使用
二、异步机制应用场景(按功能场景分类)
场景类型核心功能技术实现典型案例
网络通信浏览器与服务器的异步数据交互,不阻塞页面fetchXMLHttpRequestWebSocket- 动态加载数据(如商品列表)
- 实时聊天WebSocket(异步双向通信)
定时任务按指定时间间隔执行异步操作setTimeoutsetInterval- 轮询获取数据(如天气更新)
- 动画帧控制(requestAnimationFrame
用户交互响应鼠标、键盘等用户操作,异步执行回调事件监听(addEventListener- 按钮点击、滚动加载更多
- 表单实时验证
并行计算开启后台线程处理密集计算,不阻塞主线程(UI 渲染)Web WorkerSharedWorker- 图片处理、大数据排序
- 游戏物理引擎计算
文件操作异步读取/写入文件(主要在 Node.js 环境)fs.readFilefs.writeFile- 读取配置文件
- 写入日志数据

1. 异步编程有什么用?

有点后端基础的都知道,异步是在多线程学习时,才涉及到的概念,那么前端的这个异步有什么作用?

这里敲黑板!!!

前端的异步它并非解决多线程问题,它只是在表面上达到了多线程一样的效果,而是为了在单线程环境下实现非阻塞操作。

JavaScript主线程单线程的特性决定了同步操作会阻塞渲染,所以提出了异步的概念解决这个问题
在这里插入图片描述

JavaScript 是一种单线程语言,运行在浏览器的主线程中。这意味着在任何时刻,JavaScript 代码只能在一个线程上运行。无论是同步代码还是异步代码,它们都是在同一个线程上执行的。

总之,别把前端的异步和后端的异步扯上一点关系,当一个全新的东西去学就好了。


举个前端异步例子:

前端同步就像你洗澡,得先等热水器把水烧热,才能开始洗。要是热水器坏了,或者水烧得很慢,你就只能干等着,啥也干不了。

前端异步就好比你一边烧热水,一边看电视。热水烧着就烧着,你也不用一直盯着,可以先做别的事。等水烧好了,你再洗澡就行。这样,你的时间就被充分利用了,不会因为等待热水而浪费时间,整个过程也更高效。


2. 异步机制

异步机制 :允许在等待I/O(网络请求/文件读取)时释放主线程,保持页面渲染与处理流畅。它的核心目的是让程序在等待某个任务完成时,能够继续执行其他任务,而不是像同步代码那样一直阻塞等待。

所有异步机制最终都基于浏览器的 事件循环(Event Loop) 机制执行,这是 JS 异步编程的核心基础。

3. 前端有几种异步机制

前端中常见的异步机制包括回调函数Promiseasync/await、和 Generator。其中,Promise 和 async/await 是目前最常用和推荐的异步处理方式。

本文重点介绍前四种异步机制,最后一种说实话我还没真正用过。

3.1 回调函数(Callback)

回调函数是最传统的异步处理方式,通过将一个函数作为参数传递给另一个函数,并在异步操作完成时调用这个回调函数。

示例代码
//函数定义:fetchData 函数定义时,接受一个函数式的参数 callback。这个callback并没有具体定义,和普通参数一样,只是提供一个位置。
//这个参数是一个函数,意味着 fetchData 可以在执行过程中调用这个传入的函数。
function fetchData(callback) {//这里是故意的延时操作,模拟服务器2秒后加载回数据setTimeout(() => {console.log("数据加载完成!");callback("数据");}, 2000);
}fetchData((data) => {console.log("接收到数据:", data);
});
回调体现

函数定义:在 fetchData 函数内部,使用 setTimeout 模拟了一个异步操作(例如,从服务器加载数据)。当这个异步操作完成时(即 2 秒后),通过 callback(“数据”) 调用了传入的回调函数,并传递了 “数据” 作为参数。

回调函数的使用:在 fetchData 函数调用时,
传入了一个箭头函数 (data) => { console.log("接收到数据:", data); } 作为回调函数。
所以这个fetchData执行的步骤其实是这样的
1.进入setTimeOut函数内部
2.延时2秒
4. 输出"数据加载完成"日志
5.执行callback函数参数,而此时callback函数参数是(data) => { console.log(“接收到数据:”, data); }
6. 输出"接收到数据:数据"日志
在这里插入图片描述


顺带解释一下这里的箭头函数
箭头函数(基础知识点复习)

箭头函数是ES6引入的简洁函数语法,用=>符号定义,通常省略function关键字和return,适用于单行表达式或需要绑定外层this的场景。

例如传统函数:
function(a, b) { return a + b; }
可简化为箭头函数:
(a, b) => a + b

关键特性:

  • 无独立this,继承父级作用域的this
  • 不能用作构造函数(无prototype属性)
  • 适合回调函数或方法缩写

回调特点总结
  • 简单直接,适用于简单的异步操作。
  • 但容易出现“回调地狱”(Callback Hell),即嵌套过多的回调函数,代码难以维护。

3.2 Promise

Promise 是一种更现代的异步处理方式,它代表了一个异步操作的最终完成(或失败)及其结果值,其实写它的时候也会使用回调函数,只不过一个传统回调函数实现异步更像嵌套使用,后者是链式调用,代码可读性会好一丢丢,并且Promise有一定的定义和方法,方便处理异常情况。
在这里插入图片描述

注意这个目前Promise并没有特别合适的中文翻译,所以和别人讨论异步涉及它时,直接说这个单词就好了。

Promise状态

Promise 有三种状态:

pending:初始状态,既不是成功也不是失败。
fulfilled:操作成功完成。
rejected:操作失败,(Promise任何一个环节出现错误都会切换到这个状态,当然也可以手动reject让Promise切换成rejected状态)

状态一旦改变(从 pending 变为 fulfilled 或 rejected),就会被固化,不会再变化。

Promise状态处理

Promise 提供 .then()、.catch() 和 .finally() 处理结果:

  • .then() 可处理 fulfilled 或 rejected 状态(通过第二个回调参数,可以忽略处理rejected状态的处理,大多数人更习惯使用.catch来处理rejected状态)。
  • .catch() 是专门处理 rejected 状态的语法糖(等价于 .then(null, onRejected))。
  • .finally() 无论状态如何都会执行,用于清理操作。
示例代码:
function fetchData() {return new Promise((resolve, reject) => {setTimeout(() => {console.log("数据加载完成!");resolve("数据");// 如果出错可以用 reject("错误信息");}, 2000);});
}fetchData().then((data) => {console.log("接收到数据:", data);}).catch((error) => {console.error("发生错误:", error);});
Promise特点总结:
  • 避免了回调地狱,代码更加清晰。
  • 提供了错误处理机制(通过 .catch())。
  • 可以通过 Promise.all() 等方法同时处理多个异步操作。

3.3 async/await

async 和 await 是 ES2017(ES8)引入的 JavaScript 语法,用于简化 Promise 的使用,让异步代码看起来更像同步代码。它们本质上是 Promise 的语法糖,但提供了更直观的写法。

用我自己的角度来理解,你想让异步操作,就在函数前面加个await,如果某个函数里加了await,那就必须使用aysnc保证它异步操作生效。并且await要使用try和catch进行异常捕获。

哪里需要等待异步结果,就在哪里加 await;加了 await 的函数必须用 async 修饰;为了安全,用 try…catch 捕获可能的错误。

aysnc

async 用于声明一个异步函数,它有两个特点:

  • 自动返回 Promise:无论函数内部是否显式返回 Promise,async 函数的返回值都会被包装成 Promise。
  • 支持 await:async 函数内部可以使用 await 关键字等待 Promise 完成。
await

await 只能在 async 函数内部使用,它的作用是:

  • 暂停函数执行:等待 Promise 被解决(fulfilled 或 rejected)。
  • 返回 Promise 的结果:如果 Promise 成功,则返回其结果值;如果失败,则抛出错误(需用 try…catch 捕获)。
示例代码:
// 模拟一个异步操作(如网络请求)
function simulateNetworkRequest() {console.log("开始请求数据...");// 返回一个 Promise,表示异步操作return new Promise((resolve) => {// 使用 setTimeout 模拟 2 秒的网络延迟setTimeout(() => {console.log("服务器已返回数据!");resolve("用户信息和文章列表"); // 数据加载完成,传递结果}, 2000);});
}// 使用 async 声明异步函数
async function fetchData() {console.log("进入 fetchData 函数");// await 会暂停当前函数的执行,等待 Promise 完成const result = await simulateNetworkRequest();// 这行代码会在 Promise 成功后才执行console.log("fetchData 函数继续执行");return result;
}// 主函数
async function main() {console.log("程序开始执行");try {console.log("等待数据加载...");// 调用异步函数并等待结果const data = await fetchData();// 这行代码会在 fetchData 完全执行完毕后才运行console.log("成功获取数据:", data);} catch (error) {console.error("获取数据失败:", error);}console.log("程序执行结束");
}// 启动主函数
main();

aysnc/wait特点总结:

  • 代码更简洁,更接近同步代码的写法。
  • 使用 try...catch 可以方便地捕获错误。
  • 需要与 Promise 结合使用。

3.4 Generator(生成器)

Generator 是一种可以暂停和恢复执行的函数,虽然它本身不是直接用于异步操作,但可以通过手动控制暂停和恢复来实现异步效果。不过,Generator 的使用相对复杂,且不如 async/await 方便。

示例代码:
function* fetchData() {console.log("开始加载数据");yield new Promise((resolve) => {setTimeout(() => {console.log("数据加载完成!");resolve("数据");}, 2000);});
}const generator = fetchData();
const promise = generator.next().value;
promise.then((data) => {console.log("接收到数据:", data);
});
特点总结:
  • 可以暂停和恢复函数执行。
  • 需要手动管理暂停点和恢复逻辑。
  • 使用场景较少,通常被 async/await 替代。

异步实现方法总结

前端中常见的异步机制包括回调函数、Promise、async/await和 Generator已经基本介绍了一遍。说实话没有实践,这些理论都很难理解,就算理解了也真得很难形象地描述出来。
其中,Promiseasync/await 是目前最常用和推荐的异步处理方式,因为它们提供了更简洁、更易读的代码风格和强大的错误处理机制。


4 axios:基于Promise的HTTP客户端

前文已经说过Promiseasync/await` 是目前最常用和推荐的异步处理方式,这么常用又好用的功能自然有人会想办法使用技术和它进行对接。

axios是一个流行的基于Promise的HTTP客户端,可以用于浏览器和Node.js环境。它提供了丰富的功能,如拦截请求和响应、取消请求、自动转换JSON数据等。

4.1 axios的基本用法

axios的核心API返回Promise,因此可以与async/await无缝集成:

// GET请求
axios.get('/api/users').then(response => console.log(response.data)).catch(error => console.error(error));
// POST请求
axios.post('/api/users', { name: 'John' }).then(response => console.log('创建成功:', response.data)).catch(error => console.error('创建失败:', error));

4.2 axios与async/await的结合

axios与async/await结合使用可以简化异步请求处理:

async function getUserData(userId) {try {const userResponse = await axios.get(`/api/users/${userId}`);const user = userResponse.data;const postsResponse = await axios.get(`/api/users/${userId}/posts`);const posts = postsResponse.data;return { user, posts };} catch (error) {console.error('获取数据失败:', error);throw error;}
}

4.3 axios的优势

  1. Promise基础:所有请求都返回Promise,可以与async/await完美结合
  2. 拦截器:可以拦截请求和响应,添加自定义逻辑(如认证、日志记录)
  3. 取消请求:支持请求取消,避免不必要的处理
  4. 自动转换JSON:自动将响应数据转换为JavaScript对象
  5. 浏览器兼容性:支持老版本浏览器,提供polyfill

5 技术对比与选择指南

5.1 Promise与async/await的区别

特性Promiseasync/await
语法链式调用 .then()类同步代码
错误处理.catch()try/catch
可读性可能形成回调地狱更接近自然语言
适用场景简单异步操作复杂异步流程
基础是异步处理的基础建立在Promise之上

5.2 axios与原生fetch的对比

特性axiosfetch
Promise基础
自动JSON转换
拦截器支持不支持
错误处理自动处理网络错误需要手动检查
浏览器兼容广泛支持现代浏览器
取消请求支持需要polyfill

5.3 链式调用与同步异步的结合

在实现异步链式调用时,需要考虑:

  1. 同步链式调用:方法依次执行,每个方法返回this
  2. 异步链式调用:方法中包含异步操作,需要特殊处理执行顺序
  3. Promise链式调用:使用.then()连接多个异步操作
  4. async/await链式调用:使用await等待异步操作完成

6. 最佳实践与注意事项

6.1 异步编程的最佳实践

  1. 错误处理:始终处理异步操作中的错误,特别是在使用async/await时
  2. 并行操作:对于不依赖彼此的异步操作,使用Promise.all并行执行
  3. 取消机制:对于长时间运行的操作,实现取消机制避免资源浪费
  4. 避免过度使用await:对于并行操作,await会串行化执行,影响性能

6.2 性能优化建议

  1. 缓存结果:对于重复的异步操作,考虑缓存结果
  2. 限制并发:对于大量异步操作,限制并发数量避免资源耗尽
  3. 使用Web Workers:对于CPU密集型操作,考虑使用Web Workers
  4. 合理使用Promise.allSettled:当需要等待多个操作完成而不关心个别失败时

6.3 代码组织建议

  1. 分离异步逻辑:将异步操作封装在单独的函数中
  2. 使用中间件模式:对于复杂的异步流程,考虑使用中间件模式
  3. 避免嵌套:使用async/await减少回调嵌套
  4. 文档注释:为异步函数添加清晰的文档注释,说明返回的Promise状态

7 异步编程总结与未来展望

异步编程是现代Web开发的核心技能,Promise、async/await、axios和链式调用等技术共同构成了JavaScript异步编程的完整生态。这些技术各有优势,合理组合使用可以构建出高效、可维护的应用程序。
随着JavaScript语言的发展,异步编程模型将继续演进。异步迭代器、异步生成器等新特性为异步编程提供了更多可能性。同时,TypeScript等工具也为异步代码提供了更好的类型支持,进一步提高了开发效率和代码可靠性。
掌握这些异步编程技术,不仅能够解决当前的开发挑战,也为应对未来更复杂的异步场景奠定了坚实基础。通过不断实践和总结,开发者可以形成自己高效的异步编程模式,构建出更优秀的Web应用。

相关文章:

  • pythonday46
  • HTML+CSS 动态背景动态登录注册框
  • 模拟 AJAX 提交 form 表单及请求头设置详解
  • 大模型成长过程-预训练tokenizer
  • SQL Server 窗口函数详解:窗口行数控制的原理、关键字与应用场景
  • 鸿蒙NEXT-HMRouter,在使用router后无法跳转问题解决
  • 计算机网络-自顶向下—第四章网络层重点复习笔记
  • Python实例题:Python计算偏微分方程
  • 【Ubuntu 22.04 推荐的 apt 包管理方式详解】
  • HQL 优化:从低效到高效的蜕变之旅
  • Git可视化革命:3分钟学会用Mermaid+AI画专业分支图
  • 数据治理域——数据建模设计
  • LabVIEW工业金属腐蚀监测
  • LeetCode 第71题 简化路径(繁琐)
  • 打牙祭是什么意思
  • SCADA|信创KingSCADA4.0历史报警查询的差异
  • XCTF-misc-János-the-Ripper
  • ELK日志文件分析系统——E(Elasticsearch)
  • Karate UI测试之驱动配置
  • vulnhub-Earth
  • 杭州开发app/百度seo排名360
  • 在网站上做外贸/友情链接只有链接
  • 政府网站建设自查/网络产品运营与推广
  • 优秀企业网站制作/产品市场推广方案范文
  • 合肥专业做网站的公司哪家好/长春做网络优化的公司
  • 房地产型网站建设/企业网站模板 免费