async/await
给我们带来的最重要的好处是同步编程风格。我们来看一个例子吧。
很明显,async/await
版本比promise
版本更容易理解。如果忽略await
关键字,代码看起来就像任何其他同步语言,如Python。
好的一面不仅是可读性,async/await
有本地浏览器支持。截至今天,所有主流浏览器 查看都完全支持异步功能。
本机支持意味着您不必转换代码。更重要的是,它有助于调试。当您在函数入口设置断点并跳过await
行时,您将看到调试器在bookModel.fetchAll()
执行期间暂停一段时间,然后移动到下一 行.filter
!这比promise
情况要容易得多,在promise
情况下你必须在.filter
行设置另一个断点 。
另一个不太明显的好处是async
关键字。它声明getBooksByAuthorWithAwait()
函数返回值确保是一个promise
,以便调用者可以安全调用getBooksByAuthorWithAwait().then(...)
或await getBooksByAuthorWithAwait()
。看看下面的代码(不好的做法!):
在上面的代码中,getBooksByAuthorWithPromise
可以返回一个promise
(正常情况)或一个null
值(例外情况),在这种情况下,调用者不能安全地调用.then()
。通过async
声明,这种返回null
的情况将不可能出现。
Async/await可能会产生误导
有些文章将async/await
与Promise
进行比较,并声称它是JavaScript异步编程演变的下一代,我表示不同意。Async/await
是一种改进,但它只不过是一种语法糖,它不会完全改变我们的编程风格。
从本质上讲,await
函数仍然是promise
。在正确使用await
函数之前,您必须了解promises
,还有就是,大多数情况下您需要同时使用promises
和异步函数。
考虑上面示例中的getBooksByAuthorWithAwait()
和getBooksByAuthorWithPromises()
函数。请注意,它们不仅在功能上相同,而且具有完全相同的接口。
如果直接调用getBooksByAuthorWithAwait()
,这意味着将返回一个promise
。
嗯,这并不是坏事。只是await
这个名称让人感觉“哦,这可以将异步函数转换为同步函数”,这实际上是错误的。
Async/await的坑
那么使用async/await
时会出现什么错误?以下一些常见的情况。
太顺序了
虽然await
可以使您的代码看起来像同步,但请记住它们仍然是异步的,必须注意避免过于顺序。
此代码看起来逻辑正确。但这是错误的。
await bookModel.fetchAll()
将等到fetchAll()
返回。- 然后
await authorModel.fetch(authorId)
将被调用。 请注意,authorModel.fetch(authorId)
它不依赖于bookModel.fetchAll()
的结果,实际上它们可以并行调用!但是,通过await
在这里使用,这两个调用变为顺序,并且总执行时间将比并行版本长得多。
这是正确的方法:
或者更糟糕的是,如果你想逐个获取一个列表的项,必须依赖promise
:
简单地说,您仍然需要异步考虑工作流,然后尝试同步编写代码await
。在复杂的工作流程中,直接使用promises
可能更容易。
错误处理
使用promises
,异步函数有两个可能的返回值:已解析的值和被拒绝的值。我们可以.then()
用于正常情况,.catch()
用于特殊情况。但是,async/await
错误处理可能会很棘手。
try…catch
最标准的(我推荐的)方法是使用try...catch
语句。当一个await
调用时,任何被拒绝的值都将作为异常抛出。这是一个例子:
catch
的错误正是被拒绝的值。在我们发现异常后,有几种方法来处理它:
- 处理异常,并返回正常值。(不在
catch
块中使用任何return
语句,这等同于使用return undefined
,也是正常值。) - 抛出它,如果你想让调用者处理它。可以直接抛出普通的错误对象
throw error
,这在promise
链中允许使用async getBooksByAuthorWithAwait()
函数(即仍然可以像这样调用它getBooksByAuthorWithAwait().then(...).catch(error => ...)
); 或者可以使用Error
对象包装错误,例如throw new Error(error)
,当控制台中显示此错误时,将提供完整的堆栈跟踪。 - 拒绝它,就像
return Promise.reject(error)
。这相当于throw error
不推荐。
使用try...catch的好处是:
- 简单,传统。只要您有Java或C++等其他语言的经验,就不会有任何困难。
- 如果不需要每步执行错误处理,仍然可以在一个
try...catch
块中包装多个await
调用在一个位置处理错误。
这种方法也存在一个缺陷。由于try...catch
将捕获块中的每个异常,因此将会捕获一些通常不会被promises
捕获的异常。想想这个例子:
运行此代码,您将在控制台中收到ReferenceError: cb is not defined
错误。错误是由console.log()
输出而不是JavaScript本身。有时这可能是致命的。如果BookModel
被深深地包含在一系列函数调用中,并且其中一个调用吞噬了错误,那么找到这样的未定义错误将非常困难。
使函数返回两个值
另一种错误处理方式受Go语言的启发。它允许异步函数返回错误和结果。有关详细信息,请参阅此博客文章: How to write async await without try-catch blocks in Javascript
简言之,您可以使用这样的await
函数:
我个人不喜欢这种方法,因为它将Go风格带入JavaScript,感觉不自然,但在某些情况下,这可能非常有用。
使用.catch
我们将在这里介绍的最后一种方法是继续使用 .catch()
。
回想一下await
的功能:它将等待promise
完成其工作。再回想一下,promise.catch()
也将是一个promise
,所以我们可以像这样编写错误处理:
这种方法有两个小问题:
- 它是
promises
和await
函数的混合体。您仍然需要了解promises
的工作原理。 - 错误处理在正常路径之前进行,这样不直观。
结论
ES7引入的关键字async/await
肯定是对JavaScript异步编程的改进。它可以使代码更容易阅读和调试。然而,为了正确使用它们,必须完全理解promise
,因为它们只不过是语法糖,而潜在的技术仍然是promise
。
1、具有1-5工作经验的,面对目前流行的技术不知从何下手,
需要突破技术瓶颈的可以加。
2、在公司待久了,过得很安逸,
但跳槽时面试碰壁。
需要在短时间内进修、跳槽拿高薪的可以加。
3、如果没有工作经验,但基础非常扎实,对java工作机制,
常用设计思想,常用java开发框架掌握熟练的,可以加。
4、觉得自己很牛B,一般需求都能搞定。
但是所学的知识点没有系统化,很难在技术领域继续突破的可以加。
5. 群号:高级架构群 Java进阶群:180705916.备注好信息!送架构视频。
6.阿里Java高级大牛直播讲解知识点,分享知识,
多年工作经验的梳理和总结,带着大家全面、
科学地建立自己的技术体系和技术认知!