async 异步函数 不完全使用攻略

前言

现在已经到 8012 年的尾声了,前端各方面的技术发展也层出不穷,VueConf TO 2018 大会 也发布了 Vue 3.0的计划。而在我们(我)的日常中也经常用 Vue 来编写一些项目。那么,就少不了 ES6 的登场了。那么话说回来,你真的会用 ES6 的 async 异步函数吗?

1、async 介绍

先上 MDN 介绍:https://developer.mozilla.org...

async function 用于声明 一个 返回 AsyncFunction 对象的异步函数。异步函数是值通过事件循环异步执行的函数,它会通过一个隐式的 Promise 返回其结果。如果你的代码使用了异步函数,它的语法和结构更像是标准的同步函数

人工翻译:async 关键字是用于表示一个函数里面有异步操作的含义。它通过返回一个 Promise 对象来返回结果它的最大的特点是:通过 async / await 将异步的操作,但是写法和结构却是和我们平时写的(同步代码)是一样

2、示范


// 一般我们会把所有请求方法都定义在一个文件里,这里定义一个方法来模拟我们的日常请求
function fetch() {
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
};
// 然后在需要它的地方调用它
async function getUserInfo() {
const info = await fetch(); return info;
}
getUserInfo().then(info => console.log(info));

我们可以看到,整个过程非常直观和清晰,语句语义非常明确,整个异步操作看起来就像是同步一样。如果看完上面的流程没有问题的话,那我们接下来继续深入的了解一下。

3、async Promise setTimeout(定时器) 的结合使用情况

接下来给大家演示一道题目,这道题是我当时面某条的面试题,估计和多人也见过,这道题非常经典而且使用场景页非常多,研究意义非常大,那么我在这里就给大家分享一下。

求下面的输出结果:

async function async1(){
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2(){
console.log('async2')
}
console.log('script start')
setTimeout(function(){
console.log('setTimeout')
},0)
async1();
new Promise(function(resolve){
console.log('promise1')
resolve();
}).then(function(){
console.log('promise2')
})
console.log('script end')

这里一共有 8 条 log 语句,先别复制到控制台上,大家给20秒钟的时间默念一下输出的顺序。

1..2.. .. .. 20

我先给上正确的答案:


script start
async1 start
async2
promise1
script end
promise2
async1 end
setTimeout

如果你的答案和上面的正确答案有所偏差,那么说明你对 async / await 的理解还是不够深刻,希望你阅读完我的这篇文章之后可以直面各种同步异步问题了(嘻嘻,这还不点个赞嘛)

我们再来回顾一下 MDN 对 async / await 的描述:

当调用一个 async 函数时,会返回一个 Promise 对象。当这个 async 函数返回一个值时,Promise 的 resolve 方法会负责传递这个值;当 async 函数抛出异常时,Promise 的 reject 方法也会传递这个异常值。

async 函数中可能会有 await 表达式,这会使 async 函数暂停执行,等待 Promise 的结果出来,然后恢复async函数的执行并返回解析值(resolved)。

async/await的用途是简化使用 promises 异步调用的操作,并对一组 Promises执行某些操作。正如Promises类似于结构化回调,async/await类似于组合生成器和 promises。

await

await 操作符用于等待一个Promise 对象。它只能在异步函数 async function 中使用。


[return_value] = await expression;

await 表达式会暂停当前 async function 的执行,等待 Promise 处理完成。若 Promise 正常处理(fulfilled),其回调的resolve函数参数作为 await 表达式的值,继续执行 async function

若 Promise 处理异常(rejected),await 表达式会把 Promise 的异常原因抛出。

另外,如果 await 操作符后的表达式的值不是一个 Promise,则返回该值本身。

其中非常重要的一句是:遇到 await 表达式时,会让 async 函数 暂停执行,等到 await 后面的语句(Promise)状态发生改变(resolved或者rejected)之后,再恢复 async 函数的执行(再之后 await 下面的语句),并返回解析值(Promise的值)

这么多 Promise 相关的内容是因为async / await 是建立在 Promise 的基础上的呀~~

然后再来回头看我们的题目,会发现,有点不对劲啊


async1 end
promise2

那是因为还有一个Promise.resolve 的点没有考虑,这也是我中招的点

4、分析过程

  1. 定义一个异步函数 async1
  2. 定义一个异步函数 async2
  3. 打印 ‘script start’ // *1
  4. 定义一个定时器(宏任务,优先级低于微任务),在0ms 之后输出
  5. 执行异步函数 async1

    1. 打印 'async1 start' // *2
    2. 遇到await 表达式,执行 await 后面的 async2

      1. 打印 'async2' // *3
    3. 返回一个 Promise,跳出 async1 函数体
  6. 执行 new Promise 里的语句

    1. 打印 ‘promise1‘ // *4
    2. resolve() , 返回一个 Promise 对象,把这个 Promise 压进队列里
  7. 打印 ’script end' // *5
  8. 同步栈执行完毕
  9. 回到 async1 的函数体,async2 函数没有返回 Promise,所以把要等async2 的值 resolve,把 Promise 压进队列
  10. 执行 new Promise 后面的 .then,打印 ’promise2‘ // *6
  11. 回到 async1 的函数体,await 返回 Promise.resolve() ,然后打印后面的 ’async1 end‘ // *7
  12. 最后执行定时器(宏任务) setTimeout,打印 ’setTimeout‘ // *8

我对这段代码的过程分析大致如上(如果有什么理解不对的地方请指出),这里有很关键而且是大家容易理解错误的点是:很多人以为 await 会一直等待后面的表达式执行完之后才会执行后续代码,实际上 await 是会先执行后面的表达式,然后返回一个Promise,接着就跳出整个 async 函数来执行后面的代码,也就是说执行到 await 的时候,会有一个 让出线程 的操作。等后面的同步站执行完了之后,又会回到 async 函数中等待 await 表达式的返回值,如果不是一个 Promise 对象,则会有一个期待它 resolve 成为一个 Promise对象的过程,然后继续执行 async 函数后面的代码,直到是一个 Promise 对象,则把这个 Promise 对象放入 Promise 队列里。

所以说 ,’async1 end' 和‘promise2‘ 这个不注意就会出错的难点就是这样

那么现在,我们是不是大致上对async / await 理解了呢,我们来改一下这道题再来看看,把 async2 改造一下


async function async1(){
console.log('async1 start')
await async2()
console.log('async1 end')
}
function async2(){ // 去掉了 async 关键字
console.log('async2');
}
console.log('script start')
setTimeout(function(){
console.log('setTimeout')
},0)
async1();
new Promise(function(resolve){
console.log('promise1')
resolve();
}).then(function(){
console.log('promise2')
})
console.log('script end')

这次大家能做对了吗~

5、日常常用示例

上面写了那么多,只是为了方便大家对于异步函数的理解,

下面给一些我们日常开发中使用异步函数的例子。一般来说,我们有一个业务需要分不完成,每个步骤都是异步的,并且严重依赖于上一步的执行结果,稍有不慎就会进入回调地狱(callback hell)了,这种情况下,我们可以用 async / await 来完成


// 比如在这里场景,我们提交数据的时候先判定用户是否有这个权限,然后再进行下一步动作
async function submitData(data) {
const res = await getAuth(); // 获取授权状态
if (res....) {
const data = await submit(data);
}
toast(data.message);
}

这样就可以保证两个操作的先后顺序

或者是在 Vue 中,一些初始化的操作


async created() {
const res = await this.init(); // 获取列表等操作
const list = await this.getPage(); // 分页请求等
}

但是在使用过程中,我们会发现刚从回调地狱中解救,然后就陷入 async / await 地狱的诞生

举一个例子:


async created() {
const userInfo = await this.getUserInfo(); // 获取用户数据
const list = await this.getNewsList(); // 获取文章数据
}

表面上看,这段语法是正确的,但并不是一个优秀实现,因为它把两个没有先后顺序的一部操作强行变成同步操作了,因为这里的代码是一行接着一行执行的,想一下,我们没有必要在获取用户数据之后才去获取文章数据,它们的工作是可以同时进行的

这里给出一些常用的并发执行的实例


async created() {
const userInfo = this.getUserInfo(); // 它们都会返回 Promise 对象
const list = this.getNewsList();
await userInfo;
await list;
// ...do something
}
// 如果有很多请求的情况下可以使用 Promise.all
async created() {
Promise.all([this.getUserInfo(), this.getNewsList()]).then(()=> {
// ...do something
});
}

5、图例

6、小结

1、异步的终极解决方案

2、看起来像同步的异步操作

3、便捷的捕获错误和调试

4、支持并发执行

5、要知道避免 async / await 地狱

7、写在最后

好了,关于async 异步函数的不完全指南就说到这里了,上面所提及的内容,可能也就比较浅显的内容。而且有时候,建议大家熟练使用它,在日常开发中多使用多总结才会有沉淀的效果,都是要靠自己多练,才能熟悉使用,熟能生巧!
最后,如果大家觉得我有哪里写错了,写得不好,有其它什么建议(夸奖),非常欢迎大家补充。希望能让大家交流意见,相互学习,一起进步!
我是一名 19 的应届新人,以上就是今天的分享,新手上路中,后续不定期周更(或者是月更哈哈),我会努力让自己变得更优秀、写出更好的文章,文章中有不对之处,烦请各位大神斧正。如果你觉得这篇文章对你有所帮助,请记得点赞或者品论留言哦~。

来源:https://segmentfault.com/a/1190000017224969

前端er,你真的会用 async 吗?的更多相关文章

  1. 作为前端er,写在年末的一些话

    写惯了技术类的文章,这种总结和唠嗑型的我基本也就在年末才来一篇.说实话,这种轻松类的文章往往比技术类的要受欢迎的多,因为其受众更广,看起来也不烧脑. 说来愧疚,这一年其实产出有点低,大致就写了不到二十 ...

  2. web前端程序员真的值这么多钱吗?

    对于互联网公司来说用户就是上帝,做好客户体验一切才有可能.所以互联网公司都会把钱砸向前端,Web前端程序员也越来越受到企业争相聘用. 前端工程师工资也越来越高,目前Web前端工程师工作1~2年后通常会 ...

  3. 前端er必须知道的Git地址及常用工具地址

    商城篇(找工作必练) 开源商城 推荐指数:5星,掌握了它,可以说,今后工作中的各种需求都不是问题,工作1~2年的也可以学习其中的思路(建议收藏). 这是一个集小程序/公众号/app为一体的商城系统,包 ...

  4. 前端er是否忽略了某些东西?——读《ppk谈JavaScript》

    关于书 “不知道ppk的网站QuirksMode,说明你可能还没有真正成为资深的JavaScript程序员.” ——Roger Johansson,瑞典资深Web专家. ppk是世界级前端技术专家,W ...

  5. 写给小前端er的nodejs,mongodb后端小攻略~ (windows系统~)

    一.写在前面 迫于学校的压力,研二上准备回学校做实验发论文了,感觉真的没意思,这几天学着搞搞后端,踩了很多坑,整理一下这几天的坑以免以后再犯! 二.本文主要内容(由于是面向前端同学的,所以前端的内容就 ...

  6. 16年毕业的前端er在杭州求职ing

    来杭州也有一两个星期了,这个周末下雨,是在没地去,还是习惯性的打开电脑逛技术论坛,想想也是好久没有更新博文了... 背景 因为曾经看过一篇文章面试分享:一年经验初探阿里巴巴前端社招所以来杭州也是带有目 ...

  7. 作为一名前端er,从武汉来到深圳三个月有感

    来到深圳已经三个月了,从最开始的担心自己的能力不够怕不能够在深圳这个互联网产品及其发达的城市立足下来,到现在已经慢慢地拾起了一丁点的信心了 (虽然还有很多知识是不够的.但是相当于之前我的,我是觉得我已 ...

  8. 【前端_js】理解 JavaScript 的 async/await

    async 和 await 在干什么 任意一个名称都是有意义的,先从字面意思来理解.async 是“异步”的简写,而 await 可以认为是 async wait 的简写.所以应该很好理解 async ...

  9. 项目需求会__前端er定位的思考~

    一.页面展示-----针对前端部分:后台的东西(功能.样式)不考虑! 二.动态效果------能不能实现! 三.接口数据------怎么传数据! 四.兼容性--------兼容到哪个版本浏览器! 五. ...

随机推荐

  1. 大白话带你认识 ZooKeeper !重要概念一网打尽!

    大家好,我是 「后端技术进阶」 作者,一个热爱技术的少年. 1. 前言 相信大家对 ZooKeeper 应该不算陌生.但是你真的了解 ZooKeeper 到底有啥用不?如果别人/面试官让你给他讲讲对于 ...

  2. 超简单!基于Python搭建个人“云盘”

    1. 简介 当我们想要从本地向云服务器上传文件时,比较常用的有pscp等工具,但避免不了每次上传都要写若干重复的代码,而笔者最近发现的一个基于Python的工具updog,可以帮助我们在服务器上搭建类 ...

  3. 华为荣耀5X(畅玩版 全网通)USB调试模式如何开启教程(开发者模式 开发者选项打开)

    作者:程序员小冰,CSDN博客:http://blog.csdn.net/qq_21376985, 前一段时间,公司买了一款华为荣耀畅玩版5X全网通,进行测试.发现 拿usb数据线连接PC电脑,无法进 ...

  4. eclipse android程序运行报错:Conversion to Dalvik format failed: Unable to execute dex:

    [2013-06-19 16:59:01 - Dex Loader] Unable to execute dex: Multiple dex files define Landroid/support ...

  5. 白嫖码云Pages,两分钟的事,就能搭个百度能搜到的个人博客平台

    为了攒点钱让女儿做个富二代(笑),我就没掏钱买服务器,白嫖 GitHub Pages 搭了一个博客平台.不过遗憾的是,GitHub Pages 只能被谷歌收录,无法被百度收录,这就白白损失了一大波流量 ...

  6. 2020重新出发,NOSQL,redis高并发系统的分析和设计

    高并发系统的分析和设计 任何系统都不是独立于业务进行开发的,真正的系统是为了实现业务而开发的,所以开发高并发网站抢购时,都应该先分析业务需求和实际的场景,在完善这些需求之后才能进入系统开发阶段. 没有 ...

  7. 用Python写一个随机数字生成代码,5行代码超简单

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 第一步,安装 random 库 random库是使用随机数的Python标准库 ...

  8. JVM执行子程序

    Class文件结构 计算机只认识0和1,这个称之为本地机器NativeCode Jvm的无关性 与平台无关性是建立在操作系统上,虚拟机厂商提供了许多可以运行在各种不同平台的虚拟机,它们都可以载入和执行 ...

  9. Cassandra Vnodes在Cassandra 2.0-4.0中的演进

    Vnodes简短历史 Vnodes又叫Virtual Nodes.是Cassandra在1.2版本里引入的功能,已经在生产环境中使用了近8年了.从2.0版本开始,因为默认配置里num_tokens一般 ...

  10. 备忘录:SQL SERVER2014 出现:“Cannot find one or more components”

    目录 1. 起因 2. 解决方案 3. 备注 4. 参考 2020年9月13日 00:40:09-shanzm 1. 起因 因为卸载vs2015的时候,使用了一个VS2013/2015卸载工具Tota ...