译者按: Node.js的异步编程方式有效提升了应用性能;然而回调地狱却让人望而生畏,Promise让咱们告别回调函数,写出更优雅的异步代码;在实践过程当中,却发现Promise并不完美;技术进步是无止境的,这时,咱们有了Async/Await。javascript
为了保证可读性,本文采用意译而非直译。java
Node.js 7.6已经支持async/await了,若是你尚未试过,这篇博客将告诉你为何要用它。node
对于从未据说过async/await的朋友,下面是简介:编程
示例中,getJSON函数返回一个promise,这个promise成功resolve时会返回一个json对象。咱们只是调用这个函数,打印返回的JSON对象,而后返回"done"。json
使用Promise是这样的:小程序
const makeRequest = () => getJSON() .then(data => { console.log(data) return "done" }) makeRequest()
使用Async/Await是这样的:微信小程序
const makeRequest = async () => { console.log(await getJSON()) return "done" } makeRequest()
它们有一些细微不一样:数组
// 不能在最外层代码中使用await await makeRequest() // 这是会出事情的 makeRequest().then((result) => { // 代码 })
await getJSON()表示console.log会等到getJSON的promise成功reosolve以后再执行。promise
由示例可知,使用Async/Await明显节约了很多代码。咱们不须要写.then,不须要写匿名函数处理Promise的resolve值,也不须要定义多余的data变量,还避免了嵌套代码。这些小的优势会迅速累计起来,这在以后的代码示例中会更加明显。微信
Async/Await让try/catch能够同时处理同步和异步错误。在下面的promise示例中,try/catch不能处理JSON.parse的错误,由于它在Promise中。咱们须要使用.catch,这样错误处理代码很是冗余。而且,在咱们的实际生产代码会更加复杂。
const makeRequest = () => { try { getJSON() .then(result => { // JSON.parse可能会出错 const data = JSON.parse(result) console.log(data) }) // 取消注释,处理异步代码的错误 // .catch((err) => { // console.log(err) // }) } catch (err) { console.log(err) } }
使用async/await的话,catch能处理JSON.parse错误:
const makeRequest = async () => { try { // this parse may fail const data = JSON.parse(await getJSON()) console.log(data) } catch (err) { console.log(err) } }
下面示例中,须要获取数据,而后根据返回数据决定是直接返回,仍是继续获取更多的数据。
const makeRequest = () => { return getJSON() .then(data => { if (data.needsAnotherRequest) { return makeAnotherRequest(data) .then(moreData => { console.log(moreData) return moreData }) } else { console.log(data) return data } }) }
这些代码看着就头痛。嵌套(6层),括号,return语句很容易让人感到迷茫,而它们只是须要将最终结果传递到最外层的Promise。
上面的代码使用async/await编写能够大大地提升可读性:
const makeRequest = async () => { const data = await getJSON() if (data.needsAnotherRequest) { const moreData = await makeAnotherRequest(data); console.log(moreData) return moreData } else { console.log(data) return data } }
你极可能遇到过这样的场景,调用promise1,使用promise1返回的结果去调用promise2,而后使用二者的结果去调用promise3。你的代码极可能是这样的:
const makeRequest = () => { return promise1() .then(value1 => { return promise2(value1) .then(value2 => { return promise3(value1, value2) }) }) }
若是promise3不须要value1,能够很简单地将promise嵌套铺平。若是你忍受不了嵌套,你能够将value 1 & 2 放进Promise.all来避免深层嵌套:
const makeRequest = () => { return promise1() .then(value1 => { return Promise.all([value1, promise2(value1)]) }) .then(([value1, value2]) => { return promise3(value1, value2) }) }
这种方法为了可读性牺牲了语义。除了避免嵌套,并无其余理由将value1和value2放在一个数组中。
使用async/await的话,代码会变得异常简单和直观。
const makeRequest = async () => { const value1 = await promise1() const value2 = await promise2(value1) return promise3(value1, value2) }
下面示例中调用了多个Promise,假设Promise链中某个地方抛出了一个错误:
const makeRequest = () => { return callAPromise() .then(() => callAPromise()) .then(() => callAPromise()) .then(() => callAPromise()) .then(() => callAPromise()) .then(() => { throw new Error("oops"); }) } makeRequest() .catch(err => { console.log(err); // output // Error: oops at callAPromise.then.then.then.then.then (index.js:8:13) })
Promise链中返回的错误栈没有给出错误发生位置的线索。更糟糕的是,它会误导咱们;错误栈中惟一的函数名为callAPromise,然而它和错误没有关系。(文件名和行号仍是有用的)。
然而,async/await中的错误栈会指向错误所在的函数:
const makeRequest = async () => { await callAPromise() await callAPromise() await callAPromise() await callAPromise() await callAPromise() throw new Error("oops"); } makeRequest() .catch(err => { console.log(err); // output // Error: oops at makeRequest (index.js:7:9) })
在开发环境中,这一点优点并不大。可是,当你分析生产环境的错误日志时,它将很是有用。这时,知道错误发生在makeRequest比知道错误发生在then链中要好。
最后一点,也是很是重要的一点在于,async/await可以使得代码调试更简单。2个理由使得调试Promise变得很是痛苦:
使用await/async时,你再也不须要那么多箭头函数,这样你就能够像调试同步代码同样跳过await语句。
Async/Await是近年来JavaScript添加的最革命性的特性之一。它会让你发现Promise的语法有多糟糕,并且提供了一个直观的替代方法。
对于Async/Await,也许你有一些合理的怀疑:
Fundebug专一于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有Google、360、金山软件、百姓网等众多品牌企业。欢迎你们免费试用!
转载时请注明做者Fundebug以及本文地址:
https://blog.fundebug.com/2017/04/04/nodejs-async-await/