JavaScript async/await原理及实例解析


Posted in Javascript onDecember 02, 2020

随着Node 7的发布,越来越多的人开始研究据说是异步编程终级解决方案的 async/await。

异步编程的最高境界,就是根本不用关心它是不是异步。

async 函数就是隧道尽头的亮光,很多人认为它是异步操作的终极解决方案。

async 和 await 起了什么作用

async 起什么作用

这个问题的关键在于,async 函数是怎么处理它的返回值的!

我们当然希望它能直接通过return语句返回我们想要的值,但是如果真是这样,似乎就没 await 什么事了。所以,写段代码来试试,看它到底会返回什么:

async function testAsync() {
  return "hello async";
}
const result = testAsync();
console.log(result);

看到输出就恍然大悟了——输出的是一个 Promise 对象。

c:\var\test> node --harmony_async_await .
Promise { 'hello async' }

所以,async 函数返回的是一个 Promise 对象。从文档中也可以得到这个信息。async 函数(包含函数语句、函数表达式、Lambda表达式)会返回一个 Promise 对象,如果在函数中return一个直接量,async 会把这个直接量通过Promise.resolve()封装成 Promise 对象。

async 函数返回的是一个 Promise 对象,所以在最外层不能用 await 获取其返回值的情况下,我们当然应该用原来的方式:then()链来处理这个 Promise 对象,就像这样

testAsync().then(v => {
  console.log(v);  // 输出 hello async
});

语法很简单,就是在函数前面加上async关键字,来表示它是异步的,那怎么调用呢?async函数也是函数,平时我们怎么使用函数就怎么使用它,直接加括号调用就可以了,为了表示它没有阻塞它后面代码的执行,我们在async函数调用之后加一句console.log;

async function timeout() {
  return 'hello world'
}
timeout();
console.log('虽然在后面,但是我先执行');

打开浏览器控制台,我们看到了

JavaScript async/await原理及实例解析

async函数 timeout 调用了,但是没有任何输出,它不是应该返回 'hello world', 先不要着急,看一看timeout()执行返回了什么?把上面的 timeout()语句改为console.log(timeout())

async function timeout() {
  return 'hello world'
}
console.log(timeout());
console.log('虽然在后面,但是我先执行');

继续看控制台

JavaScript async/await原理及实例解析

原来async函数返回的是一个promise对象,如果要获取到promise返回值,我们应该用then方法,继续修改代码

async function timeout() {
  return 'hello world'
}
timeout().then(result => {
  console.log(result);
})
console.log('虽然在后面,但是我先执行');

看控制台

JavaScript async/await原理及实例解析

我们获取到了"hello world', 同时timeout的执行也没有阻塞后面代码的执行,和我们刚才说的一致。

这时,你可能注意到控制台中的Promise有一个resolved,这是async函数内部的实现原理。如果async函数中有返回一个值 ,当调用该函数时,内部会调用Promise.solve()方法把它转化成一个promise对象作为返回,但如果timeout函数内部抛出错误呢? 那么就会调用Promise.reject()返回一个promise对象,这时修改一下timeout函数

async function timeout(flag) {
  if (flag) {
    return 'hello world'
  } else {
    throw 'my god, failure'
  }
}
console.log(timeout(true)) // 调用Promise.resolve() 返回promise 对象。
console.log(timeout(false)); // 调用Promise.reject() 返回promise 对象。

控制台如下:

JavaScript async/await原理及实例解析

如果函数内部抛出错误, promise对象有一个catch方法进行捕获。

timeout(false).catch(err => {
  console.log(err)
})

async关键字差不多了,我们再来考虑await关键字,await是等待的意思,那么它等待什么呢,它后面跟着什么呢?其实它后面可以放任何表达式,不过我们更多的是放一个返回promise对象的表达式。注意await关键字只能放到async函数里面

现在写一个函数,让它返回promise对象,该函数的作用是2s之后让数值乘以2

// 2s 之后返回双倍的值
function doubleAfter2seconds(num) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(2 * num)
    }, 2000);
  } )
}

现在再写一个async函数,从而可以使用await关键字, await后面放置的就是返回promise对象的一个表达式,所以它后面可以写上doubleAfter2seconds函数的调用

async function testResult() {
  let result = await doubleAfter2seconds(30);
  console.log(result);
}

现在调用testResult函数

testResult();

打开控制台,2s之后,输出了60.

现在我们看看代码的执行过程,调用testResult函数,它里面遇到了await, await表示等一下,代码就暂停到这里,不再向下执行了,它等什么呢?等后面的promise对象执行完毕,然后拿到promise resolve的值并进行返回,返回值拿到之后,它继续向下执行。具体到我们的代码,遇到await之后,代码就暂停执行了,等待doubleAfter2seconds(30) 执行完毕,doubleAfter2seconds(30)返回的promise开始执行,2秒之后,promise resolve了,并返回了值为60,这时await才拿到返回值60,然后赋值给result,暂停结束,代码才开始继续执行,执行 console.log语句。

就这一个函数,我们可能看不出async/await的作用,如果我们要计算3个数的值,然后把得到的值进行输出呢?

async function testResult() {
  let first = await doubleAfter2seconds(30);
  let second = await doubleAfter2seconds(50);
  let third = await doubleAfter2seconds(30);
  console.log(first + second + third);
}

6秒后,控制台输出220,我们可以看到,写异步代码就像写同步代码一样了,再也没有回调地域了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
在Z-Blog中运行代码[html][/html](纯JS版)
Mar 25 Javascript
JS小功能(offsetLeft实现图片滚动效果)实例代码
Nov 28 Javascript
JavaScript将数组转换成CSV格式的方法
Mar 19 Javascript
JavaScript中数据结构与算法(四):串(BF)
Jun 19 Javascript
Bootstrap 源代码分析(未完待续)
Aug 17 Javascript
深入了解JavaScript的逻辑运算符(与、或)
Dec 20 Javascript
jquery实现左右滑动式轮播图
Mar 02 Javascript
详解vue父子组件间传值(props)
Jun 29 Javascript
微信小程序 配置顶部导航条标题颜色的实现方法
Sep 20 Javascript
Vue.extend 编程式插入组件的实现
Nov 18 Javascript
vue实现网络图片瀑布流 + 下拉刷新 + 上拉加载更多(步骤详解)
Jan 14 Javascript
Element Backtop回到顶部的具体使用
Jul 27 Javascript
通过实例解析js可枚举属性与不可枚举属性
Dec 02 #Javascript
Vue router传递参数并解决刷新页面参数丢失问题
Dec 02 #Vue.js
详解Vue3 Teleport 的实践及原理
Dec 02 #Vue.js
vue $router和$route的区别详解
Dec 02 #Vue.js
基于vue项目设置resolves.alias: '@'路径并适配webstorm
Dec 02 #Vue.js
JavaScript函数柯里化实现原理及过程
Dec 02 #Javascript
element-ui中el-upload多文件一次性上传的实现
Dec 02 #Javascript
You might like
PHP mkdir()定义和用法
2009/01/14 PHP
php 数组的指针操作实现代码
2011/02/08 PHP
CI框架在CLI下执行占用内存过大问题的解决方法
2014/06/17 PHP
php将字符串转换成16进制的方法
2015/03/17 PHP
深入浅出php socket编程
2015/05/13 PHP
php实现的日历程序
2015/06/18 PHP
php制作简单模版引擎
2016/04/07 PHP
PHP与JavaScript针对Cookie的读写、交互操作方法详解
2017/08/07 PHP
imagettftext() 失效,不起作用
2021/03/09 PHP
为开发者准备的10款最好的jQuery日历插件
2014/02/04 Javascript
js onmousewheel事件多次触发问题解决方法
2014/10/17 Javascript
js+flash实现的5图变换效果广告代码(附演示与demo源码下载)
2016/04/01 Javascript
Angularjs的ng-repeat中去除重复数据的方法
2016/08/05 Javascript
使用DeviceOne实现微信小程序功能
2016/12/29 Javascript
基于Vue2实现的仿手机QQ单页面应用功能(接入聊天机器人 )
2017/03/30 Javascript
layui实现点击按钮给table添加一行
2018/08/10 Javascript
详解JS实现简单的时分秒倒计时代码
2019/04/25 Javascript
微信小程序导航栏跟随滑动效果的实现代码
2019/05/14 Javascript
layui form.render('select', 'test2') 更新渲染的方法
2019/09/27 Javascript
前端使用crypto.js进行加密的函数代码
2020/08/16 Javascript
详解nginx配置vue h5 history去除#号
2020/11/09 Javascript
python遍历文件夹下所有excel文件
2018/01/03 Python
Python实现GUI学生信息管理系统
2020/04/05 Python
Python使用pandas和xlsxwriter读写xlsx文件的方法示例
2019/04/09 Python
Tensorflow实现神经网络拟合线性回归
2019/07/19 Python
python基于pexpect库自动获取日志信息
2021/02/01 Python
世界最大的海报和艺术印刷商店:AllPosters.com
2017/02/01 全球购物
美国最大的旗帜经销商:Carrot-Top
2018/02/26 全球购物
普天C++笔试题
2016/03/20 面试题
"火柴棍式"程序员面试题
2014/03/16 面试题
C#笔试题
2015/07/14 面试题
管理心得体会
2013/12/28 职场文书
公司联欢晚会主持词
2014/03/22 职场文书
房地产广告策划方案
2014/05/15 职场文书
开会迟到检讨书范文
2015/05/06 职场文书
Mybatis-plus在项目中的简单应用
2021/07/01 Java/Android