原文地址:https://segmentfault.com/a/1190000007535316,首先感谢原文作者对该知识的总结与分享。本文是在自己理解的基础上略作修改所写,主要为了加深对该知识点的理解。

async 和 await 在干什么

任意一个名称都是有意义的,先从字面意思来理解。async 是“异步”的简写,而 await 的意思是等待。所以应该很好理解 async 用于申明一个 function 是异步的,而 await 等待某个操作完成。

那么async/await到底是干嘛的呢?我们先来简单介绍一下。

  • async/await 是一种编写异步代码的新方法。之前异步代码的方案是回调和 promise。
  • async/await 是建立在 promise 的基础上。(对promise不熟悉的同学可以看一下这篇文章入门Promise的正确姿势
  • async/await 像 promise 一样,也是非阻塞的。
  • async/await 让异步代码看起来、表现起来更像同步代码。这正是其威力所在。

async 起什么作用

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

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

  1. <script>
  2. async function test(){
  3. return 'hello async';
  4. }
  5. let result = test();
  6. console.log(result);
  7. </script>

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

Promise {<resolved>: "hello async"}

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

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

  1. async function test(){
  2. return 'hello async';
  3. }
  4. test().then((val) => {
  5. console.log(val); //hello async
  6. })

现在回过头来想下,如果 async 函数没有返回值,又该如何?很容易想到,它会返回 Promise.resolve(undefined)

联想一下 Promise 的特点——无等待,所以在没有 await 的情况下执行 async 函数,它会立即执行,返回一个 Promise 对象,并且,绝不会阻塞后面的语句。这和普通返回 Promise 对象的函数并无二致。

那么下一个关键点就在于 await 关键字了。

await 到底在等啥

一般来说,都认为 await 是在等待一个 async 函数完成。不过按语法说明,await 等待的是一个表达式,这个表达式的计算结果是 Promise 对象或者其它值(换句话说,就是没有特殊限定)。

因为 async 函数返回一个 Promise 对象,所以 await 可以用于等待一个 async 函数的返回值——这也可以说是 await 在等 async 函数,但要清楚,它等的实际是一个返回值。注意到 await 不仅仅用于等 Promise 对象,它可以等任意表达式的结果,所以,await 后面实际是可以接普通函数调用或者直接量的。所以下面这个示例完全可以正确运行。

  1. function getSomething(){
  2. return "something";
  3. }
  4. async function testAsync(){
  5. return Promise.resolve('hello async');
  6. }
  7. async function test(){
  8. let v1 = await getSomething();
  9. let v2 = await testAsync();
  10. console.log(v1,v2);
  11. }
  12. test();
  13. console.log('我执行了');
  14.  
  15. //执行结果为:
  16. //我执行了
  17. //something,hello async

await 等到了要等的,然后呢

await 等到了它要等的东西,一个 Promise 对象,或者其它值,然后呢?我不得不先说,await 是个运算符,用于组成表达式,await 表达式的运算结果取决于它等的东西。

如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。

如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。

看到上面的阻塞一词,心慌了吧……放心,这就是 await 必须用在 async 函数中的原因。async 函数调用不会造成阻塞(也就是第13行代码不会被阻塞),它内部所有的阻塞都被封装在一个 Promise 对象中异步执行。

async/await 帮我们干了啥

作个简单的比较

上面已经说明了 async 会将其后的函数(函数表达式或 Lambda)的返回值封装成一个 Promise 对象,而 await 会等待这个 Promise 完成,并将其 resolve 的结果返回出来。

现在举例,用 setTimeout 模拟耗时的异步操作,先来看看不用 async/await 会怎么写。

  1. function takeLongTime(){
  2. return new Promise((resolve) => {
  3. setTimeout(() => resolve('long time value'),1000);
  4. })
  5. }
  6. takeLongTime().then((v) => {
  7. console.log('get:',v);
  8. })

如果改用 async/await 呢,会是这样。

  1. function takeLongTime(){
  2. return new Promise((resolve) => {
  3. setTimeout(() => resolve('long time value'),1000);
  4. })
  5. }
  6. async function test(){
  7. let v = await takeLongTime();//等待异步操作的结果,阻塞后面代码的执行
  8. console.log(v);
  9. }

眼尖的同学已经发现 takeLongTime() 没有申明为 async。实际上,takeLongTime() 本身就是返回的 Promise 对象,加不加 async结果都一样,如果没明白,请回过头再去看看上面的“async 起什么作用”。

又一个疑问产生了,这两段代码,两种方式对异步调用的处理(实际就是对 Promise 对象的处理)差别并不明显,甚至使用 async/await 还需要多写一些代码,那它的优势到底在哪?

async/await 的优势在于处理 then 链

单一的 Promise 链并不能发现 async/await 的优势,但是,如果需要处理由多个 Promise 组成的 then 链的时候,优势就能体现出来了(很有意思,Promise 通过 then 链来解决多层回调的问题,现在又用 async/await 来进一步优化它)。

假设一个业务,分多个步骤完成,每个步骤都是异步的,而且依赖于上一个步骤的结果。我们仍然用 setTimeout 来模拟异步操作:

  1. /*
  2. * 传入参数n,表示这个函数执行的时间(毫秒)
  3. * 执行的结果是 n+200,这个值将用于下一步骤
  4. */
  5. function takeLongTime(n){
  6. return new Promise((resolve) => {
  7. setTimeout(() => resolve(n + 200),n);
  8. })
  9. }
  10. function step1(n){
  11. console.log(`step1 with ${n}`);
  12. return takeLongTime(n);
  13. }
  14. function step2(n){
  15. console.log(`step2 with ${n}`);
  16. return takeLongTime(n);
  17. }
  18. function step3(n){
  19. console.log(`step3 with ${n}`);
  20. return takeLongTime(n);
  21. }

现在用 Promise 方式来实现这三个步骤的处理。

  1. function doIt(){
  2. console.time('doIt');
  3. let time1 = 300;
  4. step1(time1)
  5. .then((time2) => step2(time2))
  6. .then((time3) => step3(time3))  
  7. .then((result) => {
  8. console.log(`result is ${result}`);
  9. console.timeEnd("doIt");
  10. })
  11. }
  12.  
  13. doIt();
  14.  
  15. //执行结果为:
  16. //step1 with 300
  17. //step2 with 500
  18. //step3 with 700
  19. //result is 900
  20. //doIt: 1510.2490234375ms

输出结果 result 是 step3() 的参数 700 + 200 = 900doIt() 顺序执行了三个步骤,一共用了 300 + 500 + 700 = 1500 毫秒,和 console.time()/console.timeEnd() 计算的结果一致。

如果用 async/await 来实现呢,会是这样。

  1. async function doIt() {
  2. console.time('doIt');
  3. let time1 = 300;
  4. let time2 = await step1(time1);//将Promise对象resolve(n+200)的值赋给time2
  5. let time3 = await step1(time2);
  6. let result = await step1(time3);
  7. console.log(`result is ${result}`);
  8. console.timeEnd('doIt');
  9. }
  10.  
  11. doIt();
  12.  
  13. //执行结果为:
  14. //step1 with 300
  15. //step2 with 500
  16. //step3 with 700
  17. //result is 900
  18. //doIt: 1512.904296875ms

结果和之前的 Promise 实现是一样的,但是这个代码看起来是不是清晰得多,几乎跟同步代码一样。

还有更酷的

现在把业务要求改一下,仍然是三个步骤,但每一个步骤都需要之前每个步骤的结果。

  1. /*
  2. * 传入参数n,表示这个函数执行的时间(毫秒)
  3. * 执行的结果是 n+200,这个值将用于下一步骤
  4. */
  5. function takeLongTime(n){
  6. return new Promise((resolve) => {
  7. setTimeout(() => resolve(n + 200),n);
  8. })
  9. }
  10. function step1(n){
  11. console.log(`step1 with ${n}`);
  12. return takeLongTime(n);
  13. }
  14. function step2(m,n){
  15. console.log(`step2 with ${m} + ${n}`);
  16. return takeLongTime(m + n);
  17. }
  18. function step3(k,m,n){
  19. console.log(`step3 with ${k} + ${m} + ${n}`);
  20. return takeLongTime(k + m + n);
  21. }

这回先用 async/await 来写:

  1. async function doIt() {
  2. console.time('doIt');
  3. let time1 = 300;
  4. let time2 = await step1(time1);//将Promise对象resolve(n+200)的值赋给time2
  5. let time3 = await step2(time2,time1);
  6. let result = await step3(time3,time2,time1);
  7. console.log(`result is ${result}`);
  8. console.timeEnd('doIt');
  9. }
  10.  
  11. doIt();
  12.  
  13. //执行结果为:
  14. //step1 with 300
  15. //step2 with 500 + 300
  16. //step3 with 1000 + 500 + 300
  17. //result is 2000
  18. //doIt: 2916.655029296875ms

除了觉得执行时间变长了之外,似乎和之前的示例没啥区别啊!别急,认真想想如果把它写成 Promise 方式实现会是什么样子?

  1. function doIt() {
  2. console.time('doIt');
  3. let time1 = 300;
  4. step1(time1)
  5. .then((time2) => {
  6. return step2(time1,time2)
  7. .then((time3) => [time1,time2,time3])//step3需要用到time1,time2,time3,因此需要返回
  8. })
  9. .then((times) => {
  10. let [time1,time2,time3] = times;
  11. return step3(time1,time2,time3)
  12. })
  13. .then((result) => {
  14. console.log(`result is ${result}`);
  15. console.timeEnd('doIt');
  16. })
  17. }
  18.  
  19. doIt();
  20.  
  21. //执行结果为:
  22. //step1 with 300
  23. //step2 with 300 + 500
  24. //step3 with 300 + 500 + 1000
  25. //result is 2000
  26. //doIt: 2919.49609375ms

有没有感觉有点复杂的样子?那一堆参数处理,就是 Promise 方案的死穴—— 参数传递太麻烦了,看着就晕!

注意点

就目前来说,已经理解 async/await 了吧?但其实还有一些事情没提及——Promise 有可能 reject 啊,怎么处理呢?

await 命令后面的 Promise 对象,运行结果可能是 rejected,所以最好把 await 命令放在 try...catch 代码块中。

  1. async function myFunction() {
  2. try {
  3. await somethingThatReturnAPromise();
  4. } catch (err){
  5. console.log(err);
  6. }
  7. }
  8.  
  9. //另一种写法
  10. async function myFunction() {
  11. await somethingThatReturnAPromise().catch(function(err) {
  12. console.log(err);
  13. })
  14. }

深入理解理解 JavaScript 的 async/await的更多相关文章

  1. [转] 理解 JavaScript 的 async/await

    [From] https://segmentfault.com/a/1190000007535316      边城 2016年11月19日发布 随着 Node 7 的发布,越来越多的人开始研究据说是 ...

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

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

  3. 理解 JavaScript 的 async/await

    随着 Node 7 的发布,越来越多的人开始研究据说是异步编程终级解决方案的 async/await.我第一次看到这组关键字并不是在 JavaScript 语言里,而是在 c# 5.0 的语法中.C# ...

  4. 理解ES7中的async/await

    理解ES7中的async/await 优势是:就是解决多层异步回调的嵌套 从字面上理解 async/await, async是 "异步"的含义,await可以认为是 async w ...

  5. 【转】6 Reasons Why JavaScript’s Async/Await Blows Promises Away (Tutorial)

    原文:https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec105 ...

  6. 错误的理解引起的bug async await 执行顺序

    今天有幸好碰到一个bug,让我知道了之前我对await async 的理解有点偏差. 错误的理解 之前我一直以为  await 后面的表达式,如果是直接返回一个具体的值就不会等待,而是继续执行asyn ...

  7. 理解Task和和async await

    本文将详解C#类当中的Task,以及异步函数async await和Task的关系 一.Task的前世今生 1.Thread 一开始我们需要创建线程的时候一般是通过Thread创建线程,一般常用创建线 ...

  8. 理解C#中的 async await

    前言 一个老掉牙的话题,园子里的相关优秀文章已经有很多了,我写这篇文章完全是想以自己的思维方式来谈一谈自己的理解.(PS:文中涉及到了大量反编译源码,需要静下心来细细品味) 从简单开始 为了更容易理解 ...

  9. [转] Understanding JavaScript’s async await

    PS:Promise的用处是异步调用,这个对象使用的时候,call then函数,传一个处理函数进去,处理异步调用后的结果 Promise<Action>这样的对象呢,异步调用后的结果是一 ...

随机推荐

  1. vs2010远程调试断点无效问题

    ps:本人按照下面的方式设置成功,个人感觉写的也比较清楚 来源:http://www.cnblogs.com/OpenCoder/archive/2010/02/17/1668983.html   v ...

  2. 编码原则实例------c++程序设计原理与实践(进阶篇)

    编码原则: 一般原则 预处理原则 命名和布局原则 类原则 函数和表达式原则 硬实时原则 关键系统原则 (硬实时原则.关键系统原则仅用于硬实时和关键系统程序设计) (严格原则都用一个大写字母R及其编号标 ...

  3. ES聚合报错

    在测试Elasticsearch聚合的时候报了一个错误.具体如下: GET /megacorp/employee/_search { "aggs": { "all_int ...

  4. 网页控件很多时最好用绝对定位absolute

    否则动一个位置可能影响其他的,牵一发而动全身

  5. 题解 P1436 【棋盘分割】

    题目链接 其实呢大致思路和下面的大佬们都很像.发这篇题解的目的就是加了一点~~优化~~骗分技巧. 转移方程: 设$dp[i][j][x][y][k]$表示左上$(i,j)$,右下$(x,y)$,第$k ...

  6. centos7用docker安装单节点redis4.0.11

    [root@localhost conf]# docker search redisINDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATEDdocker.io d ...

  7. SQLServer如何清除缓存?

    --1. 将当前数据库的全部脏页写入磁盘.“脏页”是已输入缓存区高速缓存且已修改但尚未写入磁盘的数据页. -- CHECKPOINT 可创建一个检查点,在该点保证全部脏页都已写入磁盘,从而在以后的恢复 ...

  8. redis原理及实现

    1 什么是redis redis是nosql(也是个巨大的map) 单线程,但是可处理1秒10w的并发(数据都在内存中) 使用java对redis进行操作类似jdbc接口标准对mysql,有各类实现他 ...

  9. python之time和datetime的常用方法

    python之time和datetime的常用方法   一.time的常用方法: import time,datetime # 时间有三种展现方式:时间戳,时间元组,格式化的时间print(time. ...

  10. C++_标准模板库STL概念介绍5-其他库与总结

    C++还提供了其他一些类库,这些类库更加专用. 例如,头文件complex为复数提供了类模板complex,包含用于float.long和long double的具体化. 这个类提供了标准的复数运算以 ...