async异步函数

文章目录

  • 异步函数(用 async 声明的函数)
  • 异步函数的返回值
  • async/await 的使用
  • 异步函数的异常处理
  • 总结

感谢铁子阅读,觉得有帮助的话点点关注点点赞,谢谢!

异步函数(用 async 声明的函数)

异步函数的定义
使用async关键字声明的函数,称之为异步函数。在普通函数前面加上 async 关键字,就成了异步函数。语法举例:

// 写法1:函数声明的写法
async function foo1() {
}

// 写法2:表达式写法(ES5语法)
const foo2 = async function () {
}

// 写法3:表达式写法(ES6箭头函数语法)
const foo3 = async () => {
}

// 写法4:定义一个类,在类中添加一个异步函数
class Person {
  async foo4() {
  }
}

JS中的“异步函数”是一个专有名词,特指用async关键字声明的函数,其他函数则称之为普通函数。如果你在一个普通函数中定义了一个异步任务,那并不叫异步函数,而是叫包含异步任务的普通函数。

async (异步的)这个单词是 asynchronous 的缩写;相反,sync(同步的)这单词是 synchronous 的缩写。

上面的异步函数代码,执行顺序与普通函数相同,默认情况下会同步执行。如果想要发挥异步执行的作用,则需要配合 await 关键字使用。稍后我们再讲 async/await的语法。

异步函数的返回值

异步函数的返回值和普通函数差别比较大,需要特别关注。

普通函数的返回值,默认是 undefined;也可以手动 return 一个返回值,那就以手动 return的值为准。

异步函数的返回值永远是 Promise 对象。至于这个 Promise 后续会进入什么状态,那就要看情况了。主要有以下几种情况:

情况1:如果异步函数内部返回的是普通值(包括 return undefined时)或者普通对象,那么Promise 的状态为fulfilled。这个值会作为then()回调的参数。
情况2:如果异步函数内部返回的是另外一个新的 Promise,那么 Promise 的状态将交给新的 Promise 决定。
情况3:如果异步函数内部返回的是一个对象,并且这个对象里有实现then()方法(这种对象称为 thenable 对象),那就会执行该then()方法,并且根据then()方法的结果来决定Promise的状态。

另外还有一种特殊情况:

情况4:如果异步函数内部在执行时遇到异常或者手动抛出异常时,那么, Promise 处于rejected 状态。

上面这四种情况似曾相识,我们在前面学习“resolve() 传入的参数”、“then()方法的返回值”知识点时,都有类似的情况,知识都是相通的。

默认返回值
代码举例:

async function foo2() {
  // 相当于 return undefined,也相当于 return Promise.resolve(undefined)
};

async function foo3() {
  Promise.resolve('qianguyihao');
  // 相当于 return undefined,也相当于 return Promise.resolve(undefined)
};

// foo2()、foo3()都是一个Promise对象
foo2().then(res => {
  console.log(res); // 打印结果:undefined
})

foo3().then(res => {
  console.log(res); // 打印结果:undefined
})

代码解释:异步函数即便没有手动写返回值,也相当于 return Promise.resolve(undefined)。

返回普通值
比如下面这段代码:

async function foo() {
  return 'qianguyihao'
};

在这里插入图片描述
可以看到,foo() 的返回值是Promise对象,不是字符串。上面的代码等价于下面这段代码:

async function foo() {
  return Promise.resolve('qianguyihao');
};

进而,我们可以通过 Promise 对象的then()方法。代码举例如下。
举例1:(异步函数中手动 return 一个值)

async function foo() {
  return 'qianguyihao';
  // 上面这行代码相当于:return Promise.resolve('qianguyihao');
};

// foo() 是一个Promise对象
foo().then(res => {
  console.log(res); // 打印结果:qianguyihao
})

async/await 的使用

异步函数配合 await 关键字使用
我们可以在async声明的异步函数中,使用 await关键字来暂停函数的执行,等待一个异步操作完成。温馨提示:await 关键字不能在普通函数中使用,只能在异步函数中使用。

在等待异步操作期间,异步函数会暂停执行,并让出线程,使其他代码可以继续执行。一旦异步操作完成,该异步函数会恢复执行,并返回一个 Promise 对象。具体解释如下:

(1)await的后面是一个表达式,这个表达式要求是一个 Promise 对象(通常是一个封装了异步任务的Promise对象)。await执行完成后可以得到异步结果。

(2)await 会等到当前 Promise 的状态变为 fulfilled之后,才继续往下执行异步函数。

async 的返回值是 Promise 对象。

本质是语法糖
async/await 是在 ES8(即ES 2017)中引入的新语法,是另外一种异步编程解决方案。

async/await 本质是 生成器 Generator 的语法糖,是对Generator的封装。什么是语法糖呢?语法糖就是让语法变得更加简洁、更加舒服,有一种甜甜的感觉。

async/await 的写法使得编写异步代码更加直观和易于管理,避免了使用回调函数或Promise链的复杂性。认识到这一点,非常重要。

Promise、Generator、async/await的对比
我们在使用 Promise、async/await、Generator 的时候,返回的都是 Promise 的实例。

如果直接使用 Promise,则需要通过 then 来进行链式调用;如果使用 async/await、Generator,写起来更像同步的代码。

接下来,我们看看 async/await 的代码是怎么写的。

async/await 的基本用法
async 后面可以跟一个 Promise 实例对象。代码举例如下:

const request1 = function () {
  const promise = new Promise((resolve, reject) => {
    requestAjax('https://www.baidu.com/xxx_url', (res) => {
      if (res.retCode == 200) {
        // 这里的 res 是接口1的返回结果
        resolve('request1 success' + res);
      } else {
        reject('接口请求失败');
      }
    });
  });

  return promise;
};

async function requestData() {
  // 关键代码
  const res = await request1();
  return res;
}
requestData().then(res => {
  console.log(res);
});

用 async/await 封装Promise链式调用【重要】
假设现在有三个网络请求,请求2必须依赖请求1的结果,请求3必须依赖请求2的结果,如果按照ES5的写法,会有三层回调,会陷入“回调地狱”。

这种场景其实就是接口的多层嵌套调用。之前学过 Promise,它可以把原本的多层嵌套调用改进为链式调用。

而本文要学习的 async/await ,可以把原本的“多层嵌套调用”改成类似于同步的写法,非常优雅。

代码举例:

// 【公共方法层】封装 ajax 请求的伪代码。传入请求地址、请求参数,以及回调函数 success 和 fail。
function requestAjax(url, params, success, fail) {
  var xhr = new xhrRequest();
  // 设置请求方法、请求地址。请求地址的格式一般是:'https://api.example.com/data?' + 'key1=value1&key2=value2'
  xhr.open('GET', url);
  // 设置请求头(如果需要)
  xhr.setRequestHeader('Content-Type', 'application/json');
  xhr.send();
  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4 && xhr.status === 200) {
      success && success(xhr.responseText);
    } else {
      fail && fail(new Error('接口请求失败'));
    }
  };
}

// 【model层】将接口请求封装为 Promise
function requestData1(params_1) {
  return new Promise((resolve, reject) => {
    requestAjax('https://api.qianguyihao.com/url_1', params_1, res => {
      // 这里的 res 是接口返回的数据。返回码 retCode 为 0 代表接口请求成功。
      if (res.retCode == 0) {
        // 接口请求成功时调用
        resolve('request success' + res);
      } else {
        // 接口请求异常时调用
        reject({ retCode: -1, msg: 'network error' });
      }
    });
  });
}


// requestData2、requestData3的写法与 requestData1类似。他们的请求地址、请求参数、接口返回结果不同,所以需要挨个单独封装 Promise。
function requestData2(params_2) {
  return new Promise((resolve, reject) => {
    requestAjax('https://api.qianguyihao.com/url_2', params_2, res => {
      if (res.retCode == 0) {
        resolve('request success' + res);
      } else {
        reject({ retCode: -1, msg: 'network error' });
      }
    });
  });
}

function requestData3(params_3) {
  return new Promise((resolve, reject) => {
    requestAjax('https://api.qianguyihao.com/url_3', params_3, res => {
      if (res.retCode == 0) {
        resolve('request success' + res);
      } else {
        reject({ retCode: -1, msg: 'network error' });
      }
    });
  });
}

// 封装:用 async ... await 调用 Promise 链式请求
async function getData() {
  // 【关键代码】
  const res1 = await requestData1(params_1);
  const res2 = await requestData2(res1);
  const res3 = await requestData3(res2);
}

getData();

上面这段代码比较长,我们在上一章学习《Promise的链式调用》时,已经详细讲过了。

await 后面也可以跟一个异步函数
前面讲到,await后面通常是一个执行异步任务的Promise对象。由于异步函数的返回值本身就是一个Promise,所以,我们也可以在await 后面也可以跟一个异步函数。

代码举例:

const request1 = function () {
  return new Promise((resolve, reject) => {
    resolve('request1 请求成功');
  });
};

async function request2() {
  const res = await request1();
  return res;
}

async function request3() {
  // 【关键代码】request2() 既是一个异步函数,同样也是一个 Promise,所以可以跟在 await 的后面
  const res = await request2();
  console.log('res:', res);
}

request3();

异步函数的异常处理

前面讲过,如果异步函数内部在执行时遇到异常或者手动抛出异常时,那么, 这个异步函数返回的Promise 处于rejected 状态。

捕获并处理异步函数的异常时,有两种方式:

方式1:通过 Promise的catch()方法捕获异常。
方式2:通过 try catch捕获异常。

在处理异步函数的异常情况时,方式2更为常见。

如果我们不捕获异常,则会往上层层传递,最终传递给浏览器,浏览器会在控制台报错。

方式1:过 Promise的catch()方法捕获异常

function requestData1() {
  return new Promise((resolve, reject) => {
    reject('任务1失败');
  })
}

function requestData2() {
  return new Promise((resolve, reject) => {
    resolve('任务2成功');
  })
}

async function getData() {
  // requestData1 在执行时,遇到异常
  await requestData1();
  /*
  由于上面的代码在执行是遇到异常,所以,这里虽然什么都没写,底层默认写了如下代码:
  return Promise.reject('任务1失败');
  */

  // 下面这行代码不会再走了
  await requestData2();
}

// getData() 这个异步函数的返回值是一个 Promise,状态为 rejected,所以会走到 catch()
getData().then(res => {
  console.log(res);
}).catch(err => {
  console.log('err:', err);
});

打印结果:

err: 任务1失败

方式2:通过 try catch 捕获异常
如果你觉得上面的写法比较麻烦,还可以通过 try catch 捕获异常。
代码举例:

function requestData1() {
  return new Promise((resolve, reject) => {
    reject('任务1失败');
  })
}

function requestData2() {
  return new Promise((resolve, reject) => {
    resolve('任务2成功');
  })
}

async function getData() {
  try {
    // requestData1 在执行时,遇到异常
    await requestData1();
    /*
    由于上面的代码在执行是遇到异常,当前任务立即终止,所以,这里虽然什么都没写,底层默认写了如下代码:
    return Promise.reject('任务1失败');
    */

    // 下面这两代码不会再走了
    console.log('qianguyihao1');
    await requestData2();
  }
  catch (err) {
    // 捕获异常代码
    console.log('err:', err);
  }
}

getData();
console.log('qianguyihao2');

打印结果:

qianguyihao2
err1: 任务1失败

总结

在 async 函数中,不是所有的 异步任务都需要 await。如果两个任务在业务上没有依赖关系,则不需要 await;也就是说,可以并发执行,不需要线性执行,避免无用的等待。

感谢铁子阅读,觉得有帮助的话点点关注点点赞,谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/749757.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

电阻代码的谐音助记口诀

整理电子信息的课设,发现当时的笔记,记录一下,时间过得真快啊。 01234黑棕红橙黄 56789绿蓝紫灰白 银色和金色代表误差, 银色百分之十 金色百分之五 可以这么理解,运动会奖牌,金牌比银牌等级高&#xff…

Django(根据Models中模型类反向生成数据库表)—— python篇

一、数据库的配置 1、 django默认支持 sqlite,mysql, oracle,postgresql数据库。 sqlite:django默认使用sqlite的数据库,默认自带sqlite的数据库驱动 , 引擎名称:django.db.backends.sqlite3 mysql:引擎名称&#xff…

python实训day5

1、 from ming import getconn conn getconn("gaoming") print() sql [("select * from dept", ()),#"dept"的表中选择所有列("delete from person where sid<%s", (4,)),#删除"person"表中"sid"列小于4的记…

【JavaScript】JS对象和JSON

目录 一、创建JS对象 方式一&#xff1a;new Object() 方式二&#xff1a;{属性名:属性值,...,..., 方法名:function(){ } } 二、JSON格式 JSON格式语法&#xff1a; JSON与Java对象互转: 三、JS常见对象 3.1数组对象API 3.2 其它对象API 一、创建JS对象 方式一&#xff1a;new…

君諾外匯:为什么巴菲特现在加倍下注油气股票?油价上涨是主因吗?

近年来&#xff0c;以巴菲特为代表的一些顶级投资者开始在能源领域加大投资力度&#xff0c;特别是油气股票。这一转变引发了广泛关注&#xff0c;特别是在油价上涨的背景下。本文将Juno markets外匯深入分析巴菲特投资策略的变化原因&#xff0c;探讨其在能源市场的布局及未来…

如何用Vue3和Plotly.js实现一个动态3D图的在线展示

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 基于 Plotly.js 的交互式图表动画 应用场景 本代码演示了如何使用 Plotly.js 创建交互式图表动画&#xff0c;其中一个区域填充的区域在给定时间间隔内更新其数据。这种动画可用于可视化时间序列数据或展示数…

冷门赛道,视频号励志语录赛道详解,新手轻松上手

大家好&#xff0c;我是闷声轻创&#xff0c;在当今数字化时代&#xff0c;社交媒体已成为人们获取信息、分享生活和实现个人价值的重要渠道。视频号&#xff0c;作为新兴的短视频平台&#xff0c;以其独特的优势和巨大的流量潜力&#xff0c;吸引了众多创作者的目光。今天我将…

华为畅享系列多款产品升级HramonyOS 4.2版本,一篇带你解读

最近华为畅享系列多款手机陆续迎来了HarmonyOS 4.2新版本&#xff0c;华为畅享70S、华为畅享70 Pro、华为畅享60X、华为畅享60 Pro和华为畅享50 Pro都在升级计划中。这次升级的4.2版本不仅功能强大&#xff0c;重点是好玩又实用&#xff0c;速来围观&#xff01; 那本次升级版本…

基于JSP的水果销售管理网站

开头语&#xff1a;你好呀&#xff0c;我是计算机学长猫哥&#xff01;如果有相关需求&#xff0c;文末可以找到我的联系方式。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;JSP技术 工具&#xff1a;B/S架构 系统展示 首页 管理员功能模块 用户前…

使用原子子表创建可重用的子组件

原子子表是一个图形对象&#xff0c;可帮助您在Stateflow图表中创建独立的子部件。原子子表允许&#xff1a; 对具有多个状态或层次结构的图表进行微小更改后&#xff0c;模拟速度更快。 在多个图表和模型中重复使用相同的状态或子表。 易于团队开发&#xff0c;适用于在同一图…

聊一聊UDF/UDTF/UDAF是什么,开发要点及如何使用?

背景介绍 UDF来源于Hive&#xff0c;Hive可以允许用户编写自己定义的函数UDF&#xff0c;然后在查询中进行使用。星环Inceptor中的UDF开发规范与Hive相同&#xff0c;目前有3种UDF&#xff1a; A. UDF--以单个数据行为参数&#xff0c;输出单个数据行&#xff1b; UDF&#…

GMSB文章六:微生物SCFA关联分析

欢迎大家关注全网生信学习者系列&#xff1a; WX公zhong号&#xff1a;生信学习者Xiao hong书&#xff1a;生信学习者知hu&#xff1a;生信学习者CDSN&#xff1a;生信学习者2 介绍 微生物短链脂肪酸&#xff08;SCFAs&#xff09;是由肠道微生物发酵膳食纤维、抗性淀粉、低…

@城规人快来抄作业!转GIS开发月薪12000+

从性价比极低的时薪&#xff0c;到相对稳定的月薪过万&#xff0c;我做对了哪些事情&#xff1f; 今天分享的是城乡规划专业的L拿到GIS开发高薪offer的故事。 初识新中地 该同学是城乡规划专业本科&#xff0c;下面称他为L同学。 L同学是今年夏天在网络上了解了GIS开发和新…

Kafka入门到精通(四)-SpringBoot+Kafka

一丶IDEA创建一个空项目 二丶添加相关依赖 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springf…

MySQL改密

这里写目录标题 更改登录密码&#xff1a;有权限账号能登录mysql中&#xff1a;有权限账号不能登录mysql中&#xff1a;mysql5.6版本命令mysql5.7版本命令修改密码8.0版本改完后&#xff1a; mysql登录不上了本机安装了5.6后&#xff0c;又安装了mysql8.0 更改登录密码&#xf…

双麒麟系统!RK3588+银河麒麟/开放麒麟,全国产让您的产品更具竞争力

01 银河麒麟嵌入式系统介绍 银河麒麟嵌入式操作系统V10 SP1是为物联网及工业互联网场景设计的安全实时系统&#xff0c;基于Linux内核&#xff0c;采用“分域虚拟化 多域隔离”架构&#xff0c;结合了Linux的丰富生态和RTOS的硬实时能力。 该系统支持主流嵌入式芯片&#x…

“数字政协”平台如何提高政协工作效率?正宇软件助力建设!

随着信息技术的飞速发展&#xff0c;数字化已成为推动各行各业转型升级的重要力量。在政协工作中&#xff0c;数字政协平台的建设与运用&#xff0c;正成为提高政协工作效率、促进民主协商的重要手段。本文将从数字政协平台的功能特点、优势分析以及实践应用等方面&#xff0c;…

【Android】【Compose】Compose里面的Row和Column的简单使用

内容 Row和Column的简单使用方式和常用属性含义 Row 在 Jetpack Compose 中&#xff0c;Row 是一种用于在水平方向排列子元素的布局组件。它类似于传统 Android 中的 LinearLayout&#xff0c;但更加灵活和强大。 Row的代码 Composable inline fun Row(modifier: Modifier…

小九首度回应与小水分手传闻揭秘

#小九首度回应&#xff01;与小水分手传闻揭秘#近日&#xff0c;泰国娱乐圈掀起了一股热议的狂潮&#xff01;传闻中的“金童玉女”组合——“小水”平采娜与“小九”NINE疑似分手的消息&#xff0c;如同巨石投入平静的湖面&#xff0c;激起了千层浪花。而在这股狂潮中&#xf…

高效同步的PWM升压DC/DC转换器 SD6201/SD6201-AF

SD6201是高效同步的PWM升压DC/DC转换器优化为介质提供高效的解决方案电力系统。这些设备在输入电压介于0.9V和4.4V之间&#xff0c;带有1.4MHz固定频率切换。这些功能通过允许使用小型、薄型电感器以及陶瓷电容器。自动PWM/PFM轻负载下的模式切换可节省电力提高了效率。电压在2…