译者按: 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()
复制代码
它们有一些细微不一样:数组
函数前面多了一个async关键字。await关键字只能用在async定义的函数内。async函数会隐式地返回一个promise,该promise的reosolve值就是函数return的值。(示例中reosolve值就是字符串"done")promise
第1点暗示咱们不能在最外层代码中使用await,由于不在async函数内。微信
// 不能在最外层代码中使用await
await makeRequest()
// 这是会出事情的
makeRequest().then((result) => {
// 代码
})
复制代码
await getJSON()表示console.log会等到getJSON的promise成功reosolve以后再执行。
由示例可知,使用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以及本文地址: blog.fundebug.com/2017/04/04/…