ES7异步/ AWAIT使我们的开发人员编写异步JS代码看起来同步。在目前的JS版本中,我们将介绍Promises,这样我们可以简化Async流程并避免回调地狱。
回调地狱是用来描述JS中的以下情况的术语:
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语句,需要执行其他Async方法,如果某些来自callbackA的结果等于'foo'。
承诺救援
凭借承诺和ES6,我们可以简化我们以前的代码噩梦,如下所示:
function asyncTask(cb) {
asyncFuncA.then(AsyncFuncB)
.then(AsyncFuncC)
.then(AsyncFuncD)
.then(data => cb(null, data)
.catch(err => cb(err));
}
看起来好多了,你不觉得
但是在现实世界的场景中,异步流可能会更复杂一些,例如在服务器模型(nodejs)中,您可能希望将实体保存到数据库,然后根据保存的值查找其他实体,如果该值存在执行其他异步任务,所有任务完成后,您可能希望使用步骤1中创建的对象对用户进行响应。如果您希望通知用户确切错误的步骤之一发生错误。
有承诺,当然,它会看起来更清洁,然后用简单的回调,但仍然可以得到一个凌乱的IMHO。
ES7异步/等待
注意:为了享受异步/等待,您将需要使用透明机,您可以使用所需的多媒体文字或者打字稿。
这是我发现异步等待真正有用的地方,它允许你编写如下代码:
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);
}
上面的代码看起来更清洁,但是,如何处理错误?
在执行异步调用时,在执行承诺期间可能会发生什么(数据库连接错误,数据库模型验证错误等)
由于异步函数正在等待Promises,当承诺遇到错误时,会抛出一个异常,该异常将被捕获在承诺的catch方法中。
在异步/等待功能中,通常使用try / catch块来捕获这样的错误。
我不是来自一个类型的语言背景,所以try / catch增加了我附加的代码,在我看来,没有看起来干净。我确定这是个人偏好的问题,但这是我的意见。
所以以前的代码看起来像这样:
async function asyncTask(cb) {
try {
const user = await UserModel.findById(1);
if(!user) return cb('No user found');
} catch(e) {
cb('Unexpected error occurred');
}
try {
const savedTask = await TaskModel({userId: user.id, name: 'Demo Task'});
} catch(e) {
cb('Error occurred while saving task');
}
if(user.notificationsEnabled) {
try {
await NotificationService.sendNotification(user.id, 'Task Created');
} catch(e) {
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) {
cb('Error while sending notification');
}
}
cb(null, savedTask);
}
一种不同的做事方式
最近我一直用go-lang编码,真的很喜欢他们的解决方案,看起来像这样:
data, err := db.Query("SELECT ...")
if err != nil { return err }
我认为它更干净,然后使用try-catch块,并减少代码集,这使得它可读和可维护。
但是等待的问题是,如果没有提供try-catch块,它将默认退出你的功能。除非提供catch子句,否则您将无法控制它。
当我和Tomer Barnea一个好朋友坐下来,试图找到一个更清洁的解决方案,我们完成了使用下一个方法:
请记住,等待着等待解决的承诺?
有了这些知识,我们可以使小功效来帮助我们抓住这些错误:
// to.js
export default function to(promise) {
return promise.then(data => {
return [null, data];
})
.catch(err => [err]);
}
效用函数接收到一个承诺,然后将返回数据作为第二个项目解析成一个数组的成功响应。而收到的错误是第一个。
然后我们可以使我们的异步代码看起来像这样:
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);
}
上面的例子只是解决方案的一个简单用例,您可以在to.js方法中附加拦截器,该方法将接收原始错误对象,记录或执行任何您需要执行的操作,然后再传回。
我们为此库创建了一个简单的NPM包,您可以使用:Github Repo进行安装
npm i await-to-js
本文探讨了如何在不使用try-catch块的情况下处理JavaScript中的异步等待错误。通过介绍Promise的救援方法和ES7的async/await语法,文章展示了如何编写更清晰、更易于维护的异步代码。作者分享了一种灵感来自Go语言的解决方案,利用辅助函数来捕获和处理错误,从而保持代码的整洁和可读性。

567

被折叠的 条评论
为什么被折叠?



