原文来自: blog.grossman.io/how-to-writ…javascript
在ES7的中,咱们可使用async & await进行编写异步函数,使用这种写法咱们的异步函数看起来就跟同步代码同样。java
在以前的版本(ES6),可使用Promise写法,来简化咱们异步编程的流程,同时也避免了回调地狱。node
回调地狱是语义化产生的一个术语,它的释义能够用下面这种状况进行阐述:git
function AsyncTask() { asyncFuncA(function(err, resultA){ if(err) return cb(err); asyncFuncB(function(err, resultB){ if(err) return cb(err); asyncFuncC(function(err, resultC){ if(err) return cb(err); // And so it goes.... }); }); }); } 复制代码
上例代码中, 不断的回调,使得代码维护和管理控制流程变得十分的困难。 咱们不妨考虑下这种状况,假如某个if语句须要执行其余的方法,而回调函数FunctionA的结果为foo。github
ES6和Promise的出现,使得咱们能够简化以前"回调地狱"般的代码以下:数据库
function asyncTask(cb) { asyncFuncA.then(AsyncFuncB) .then(AsyncFuncC) .then(AsyncFuncD) .then(data => cb(null, data) .catch(err => cb(err)); } 复制代码
这样编写是否是看起来舒服多了?npm
可是在实际的业务场景中,异步流的处理可能会更加复杂一些。举例来讲,编程
假如在你的一个(node.js)服务器中,你可能想要:数组
在使用了Promise语法后,这样固然看起来更加的简洁了,可是,在我看来仍然有一点混乱。promise
您须要使用转译器才能使用Async/Await,您可使用babel插件或Typescript来添加所需的工具。
此时, 若使用 async/await, 你会发现代码写起来舒服多了. 它容许咱们像下面同样编写代码:
async function asyncTask(cb) { const user = await UserModel.findById(1); if(!user) return cb('No user found'); const savedTask = await TaskModel({userId: user.id, name: 'Demo Task'}); if(user.notificationsEnabled) { await NotificationService.sendNotification(user.id, 'Task Created'); } if(savedTask.assignedUser.id !== user.id) { await NotificationService.sendNotification(savedTask.assignedUser.id, 'Task was created for you'); } cb(null, savedTask); } 复制代码
上面的代码看起来可读性加强了很多, 可是如何处理错误报错呢?
在执行异步任务使用Promise的时候可能会发生一些错误相似数据库链接出错,数据库模型验证错误等状况。
当一个异步函数正在等待Promise返回值的时候,当Promise方法报了错误的时候,它会抛出异常,这个异常能够在catch方法里面捕获到。
在使用Async/Await时,咱们一般使用try/catch语句进行异常捕获。
try{ //do something } catch{ // deal err } 复制代码
我没有编写强类型语言的背景,所以增长额外的try/catch
语句, 对我来讲增长了额外的代码,这在我看来很是的冗余不干净。 我相信这多是我的喜爱的缘由,但这是我对此的见解。
因此以前的代码看起来像这样:
async function asyncTask(cb) { try { const user = await UserModel.findById(1); if(!user) return cb('No user found'); } catch(e) { return cb('Unexpected error occurred'); } try { const savedTask = await TaskModel({userId: user.id, name: 'Demo Task'}); } catch(e) { return cb('Error occurred while saving task'); } if(user.notificationsEnabled) { try { await NotificationService.sendNotification(user.id, 'Task Created'); } catch(e) { return cb('Error while sending notification'); } } if(savedTask.assignedUser.id !== user.id) { try { await NotificationService.sendNotification(savedTask.assignedUser.id, 'Task was created for you'); } catch(e) { return cb('Error while sending notification'); } } cb(null, savedTask); } 复制代码
最近我一直在使用go-lang
进行编码,而且很是喜欢他们的解决方案,它的代码看起来像这样:
data, err := db.Query("SELECT ...") if err != nil { return err } 复制代码
我认为它比使用try/catch
语句块更加简洁,而且代码量更少,这使得它可读和可维护更好。
可是使用Await的话,若是没有为其提供try-catch处理异常的话,当程序发生错误的时候,它会默默的退出(你看到不抛出的异常)。假如你没有提供catch语句来捕捉错误的话,你将没法控制它。
当我和Tomer Barnea(个人好朋友)坐在一块儿并试图找到一个更简洁的解决方案时,咱们获得了下一个使用方法: 请记住: Await在等待一个Promise返回值
有了这些知识,咱们就能够制做一个小的通用函数来帮助咱们捕捉这些错误。
// to.js export default function to(promise) { return promise.then(data => { return [null, data]; }) .catch(err => [err]); } 复制代码
这个通用函数接收一个Promise,而后将处理成功的返回值以数组的形式做为附加值返回,而且在catch方法中接收到捕捉到的第一个错误。
import to from './to.js'; async function asyncTask(cb) { let err, user, savedTask; [err, user] = await to(UserModel.findById(1)); if(!user) return cb('No user found'); [err, savedTask] = await to(TaskModel({userId: user.id, name: 'Demo Task'})); if(err) return cb('Error occurred while saving task'); if(user.notificationsEnabled) { const [err] = await to(NotificationService.sendNotification(user.id, 'Task Created')); if(err) return cb('Error while sending notification'); } cb(null, savedTask); } 复制代码
上面的例子只是一个使用该解决方案的简单用例,你能够在io.js中添加拦截方法(相似调试的断点),该方法将接收原始错误对象,打印日志或者进行其余任何你想要进行的操做,而后再返回操做后的对象。
咱们为这个库建立了一个简单的NPM包(Github Repo),您可使用如下方法进行安装:
npm i await-to-js
复制代码
这篇文章只是寻找Async/Await功能的一种不一样方式,彻底基于我的意见。 您可使用Promise,仅使用try-catch和许多其余解决方案来实现相似的结果。 只要你喜欢而且它适用。
async/await 结合promise使用就行了, 即异步流程控制的最后加上 promise的catch.
async function task(){ return await req(); } task().catch(e => console.error(e)) 复制代码
async function hello(flag){ return new Promise((resolve, reject) => { if(flag) setTimeout(() => resolve('hello'), 100); else reject('hello-error'); }) } async function demo(flag){ return new Promise((resolve, reject) => { if(flag) setTimeout(() => resolve('demo'), 100); else reject('demo-error'); }) } async function main(){ let res = await hello(1).catch(e => console.error(e)); console.log('res => ', res); let result = await Promise.all([hello(1), demo(1)]); // let result = await Promise.all([hello(1), demo(0)]).catch(e => console.error('error => ', e)); console.log('result => ', result); } main() 复制代码