异步编程:从Promise到async/await的进化

前言

async/await 被引入之前,JavaScript 开发者主要依赖于回调函数和 Promise 来处理异步操作,如网络请求、文件读写等。尽管 Promise 相对于回调函数有了显著的改进,让异步代码更加结构化,但编写和阅读包含大量嵌套或链式 Promise 的代码仍然相对复杂,尤其是涉及错误处理时。async/await 便在 ES2017 中被引入,旨在进一步简化异步代码的编写,使其读起来更像是同步代码,从而提高代码的可读性和可维护性。

使用规范

当使用 async 关键字定义一个函数时,直接从async函数返回的非Promise值会被自动封装成 resolved 状态的 Promise,而直接返回的 Promise 本身的状态则取决于该 Promise 的处理逻辑。不了解Promise的同学可以先了阅读一下为什么我们需要Promise(一) - 掘金 (juejin.cn)学些Promise的作用和语法再阅读会更好理解。

自定义异步函数

javascript 复制代码
async function helloAsync() {
    return 'Hello, Async!';
}

helloAsync().then(message => console.log(message)); // 输出: Hello, Async!
javascript 复制代码
async function helloAsync() {
  return new Promise((reslove,reject)=>{
  })
}

helloAsync().then(message => console.log(message)); // 输出为空
javascript 复制代码
async function helloAsync() {
  return new Promise((reslove,reject)=>{
    reject('Error')
  })
}

helloAsync().then(()=>{},message => console.log(message)); // 输出:Error

异步等待

await 遇到一个 Promise(或 Promise-like 对象)时,它会使当前 async 函数的执行暂停,直到该 Promise 完成(解析或拒绝)。这意味着紧跟着在 await 后的代码不会立即执行,而是等待 Promise 的结果。

ini 复制代码
async function fetchData() {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    console.log(data);
}
fetchData();

并发执行

假设你需要同时从多个API获取数据,这些操作之间相互独立,不需要等待一个完成后再开始另一个。这时,使用 Promise.all() 可以并行执行这些请求,从而减少总等待时间。

javascript 复制代码
async function fetchMultipleResources() {
    const [usersResponse, postsResponse] = await Promise.all([
        fetch('https://api.example.com/users'),
        fetch('https://api.example.com/posts')
    ]);
}
fetchMultipleResources();

异常处理

由于async await没有像Promise一样链式调用catch的错误捕获不会机制,但是我们强调在 async 函数中使用 try...catch 来捕获 Promise 中的异常,提高错误处理的可读性和健壮性。

javascript 复制代码
async function fetchDataWithErrorHandling() {
    try {
        const response = await fetch('https://api.example.com/data');
        const data = await response.json();
        console.log(data);
    } catch (error) {
        console.error('An error occurred:', error);
    }
}

为什么需要 async/await

async/await 之前,开发者主要依靠 Promise 链式调用(.then().catch())来处理异步操作的结果。这种方式虽然解决了回调地狱的问题,但在处理多层嵌套的异步操作时,代码仍然可能变得难以理解和维护。例如,多个异步操作依次执行时,每个操作后都需要跟随着 .then(),这会导致代码层次很深,阅读和维护成本增加。

举个例子:我们需要从三个不同的 API 获取数据,并且每个请求必须等待前一个请求成功后才能开始。

使用传统的 Promise 链式调用:

javascript 复制代码
function fetchFirstData() {
  return new Promise((resolve, reject) => {
      setTimeout(() => resolve("First Data"), 1000); // 模拟异步操作
  });
}

function fetchSecondData() {
  return new Promise((resolve, reject) => {
      setTimeout(() => resolve("Second Data"), 1500); // 模拟异步操作
  });
}

function fetchThirdData() {
  return new Promise((resolve, reject) => {
      setTimeout(() => resolve("Third Data"), 2000); // 模拟异步操作
  });
}

fetchFirstData()
  .then(firstData => {
      console.log(firstData);
      return fetchSecondData();
  })
  .then(secondData => {
      console.log(secondData);
      return fetchThirdData();
  })
  .then(thirdData => {
      console.log(thirdData);
  })
  .catch(error => {
      console.error("An error occurred:", error);
  });

使用 async/await 的例子:

javascript 复制代码
async function fetchAllData() {
  try {
      const firstData = await fetchFirstData();
      console.log(firstData);
      const secondData = await fetchSecondData();
      console.log(secondData);
      const thirdData = await fetchThirdData();
      console.log(thirdData);
  } catch (error) {
      console.error("An error occurred:", error);
  }
}

// 调用这个异步函数
fetchAllData();

总结

上述例子可以明显的看到在 fetchAllData 函数中,通过 await 关键字,我们可以按顺序等待每个异步操作完成,而无需嵌套 .then(),这让代码看起来就像是同步执行一样,极大地提高了可读性。错误处理也变得更加集中,使用一个 try...catch 块即可捕获整个过程中的任何错误。只有等前面的异步操作完成后,后面的异步操作才能继续,这样的代码可读性和可维护性也是最高的

相关推荐
青鱼入云7 分钟前
【面试场景题】支付&金融系统与普通业务系统的一些技术和架构上的区别
面试·金融·架构
智能化咨询13 分钟前
【Linux】【实战向】Linux 进程替换避坑指南:从理解 bash 阻塞等待,到亲手实现能执行 ls/cd 的 Shell
前端·chrome
Anson Jiang16 分钟前
浏览器标签页管理:使用chrome.tabs API实现新建、切换、抓取内容——Chrome插件开发从入门到精通系列教程06
开发语言·前端·javascript·chrome·ecmascript·chrome devtools·chrome插件
掘金安东尼19 分钟前
黑客劫持:周下载量超20+亿的NPM包被攻击
前端·javascript·面试
剑亦未配妥1 小时前
移动端触摸事件与鼠标事件的触发机制详解
前端·javascript
在未来等你5 小时前
Elasticsearch面试精讲 Day 17:查询性能调优实践
大数据·分布式·elasticsearch·搜索引擎·面试
人工智能训练师7 小时前
Ubuntu22.04如何安装新版本的Node.js和npm
linux·运维·前端·人工智能·ubuntu·npm·node.js
Seveny077 小时前
pnpm相对于npm,yarn的优势
前端·npm·node.js
yddddddy8 小时前
css的基本知识
前端·css
昔人'8 小时前
css `lh`单位
前端·css