我喜欢Typescript等中提供的新Async/Await功能的平坦性。但是,我不确定我是否喜欢这样一个事实,我必须在await块的外部声明变量我为try...catch才能使用它后来。像这样:

let createdUser
try {
    createdUser = await this.User.create(userInfo)
} catch (error) {
    console.error(error)
}

console.log(createdUser)
// business
// logic
// goes
// here


如果我错了,请纠正我,但是似乎最好的做法是不要在try主体中放置多行业务逻辑,因此我只剩下在块外声明createdUser,在块中分配它,然后在以后使用它的选择。

这种情况下的最佳实践是什么?

评论

“最佳实践”是使用有效的,可理解的,可维护的等等。我们如何“正确”回答这个问题?我只用var,知道变量会被吊起。是“错”吗?

try / catch应该完全包含您要捕获异常的内容。如果您要明确查找来自this.User.create()的错误,则不会在try / catch中放入任何其他内容。但是,将大量逻辑放入try块中也是一种完全合理的设计。这完全取决于您要如何/在何处处理错误以及如何设计异常处理代码以及对给定操作有意义的内容。没有通用的最佳实践。一种通用的最佳实践是确保以某种适当的方式捕获并处理所有错误。

async / await是ES2017(今年发行)的一部分,而不是ES6(两年前发行的)。

#1 楼


最好的做法是不要在try主体中放置多行业务逻辑。


实际上我会这样说。通常,您希望使用值使用catch所有异常:

try {
    const createdUser = await this.User.create(userInfo);

    console.log(createdUser)
    // business logic goes here
} catch (error) {
    console.error(error) // from creation or business logic
}


如果您只想捕获并处理来自promise的错误,则有以下三种选择:



在外部声明变量,并根据是否存在异常进行分支。可以采用各种形式,例如


catch块中为变量分配默认值

return提前或重新throw来自catch块的异常
设置catch块是否捕获到异常的标志,并在if条件下对其进行测试
测试要分配的变量的值



let createdUser; // or use `var` inside the block
try {
    createdUser = await this.User.create(userInfo);
} catch (error) {
    console.error(error) // from creation
}
if (createdUser) { // user was successfully created
    console.log(createdUser)
    // business logic goes here
}



测试捕获到的异常的类型,并根据该异常进行处理或重新抛出。

try {
    const createdUser = await this.User.create(userInfo);
    // user was successfully created
    console.log(createdUser)
    // business logic goes here
} catch (error) {
    if (error instanceof CreationError) {
        console.error(error) // from creation
    } else {
        throw error;
    }
}



不幸的是,标准JavaScript(仍然)不支持条件异常的语法。


then与两个回调一起使用,而不是try / catch。这确实是最丑陋的方式,我个人的建议还出于其简单性和正确性,而不是依靠标记的错误或结果值的外观来区分承诺的履行与否:

await this.User.create(userInfo).then(createdUser => {
    // user was successfully created
    console.log(createdUser)
    // business logic goes here
}, error => {
    console.error(error) // from creation
});


当然,它带有引入回调函数的缺点,这意味着您不能轻易地通过外部函数轻松地进行break / continue循环或执行早期的return



评论


您的最后一个示例使用.then()来解决诺言并提供回调,因此,也许await在那里无效。

–装饰
18年1月24日在14:57

@dcorking正在等待.then(...)调用返回的承诺。

–贝尔吉
18年1月24日在20:32

.then()调用返回的lambda是否需要async关键字?

–装饰
18年1月25日在7:56

我见过有人直接把捕获处理程序附加到等待中。这样做或将其包装在try / catch中是一个好主意吗?

– Saroj
19-2-25在17:44

@Saroj const result =等待某事().catch(err => fallback);比让结果简单尝试{结果=等待某事(); } catch(err){结果=后备;因此,在那种情况下,我认为这是一个好主意。

–贝尔吉
19年2月25日在18:08

#2 楼

另一种更简单的方法是将.catch追加到promise函数。例如:

const createdUser = await this.User.create(userInfo).catch( error => {
// handle error
})


#3 楼

我通常会在失败时使用Promise的catch()函数返回具有error属性的对象。例如,在您的情况下,我会这样做:

 const createdUser = await this.User.create(userInfo)
          .catch(error => { error }); // <--- the added catch

if (Object(createdUser).error) {
    console.error(error)
}
 


如果您不想继续添加catch()调用,则可以在该函数的原型中添加一个辅助函数:

 Function.prototype.withCatcher = function withCatcher() {
    const result = this.apply(this, arguments);
    if (!Object(result).catch) {
        throw `${this.name}() must return a Promise when using withCatcher()`;
    }
    return result.catch(error => ({ error }));
};
 


现在您可以执行以下操作:

 const createdUser = await this.User.create.withCatcher(userInfo);
if (Object(createdUser).error) {
    console.error(createdUser.error);
}
 




编辑03/2020

您还可以添加默认的“捕获到错误对象”函数,如下所示:

 Promise 


然后如下使用它:

 Promise.prototype.catchToObj = function catchToObj() {
    return this.catch(error => ({ error }));
};
 


评论


我使用了最后一种方法,它给了我“ catchToObj”不是函数错误。

– newguy
5月17日6:53

您在我的答案中调用第一个代码段后,@ newguy catchToObj将存在于每个Promise对象上。如果您的函数没有返回Promise,它将无法正常工作

–阿里克
5月17日10:13

我正在使用Sequelize的create方法,该方法返回Promise 。定义是:公共静态异步创建(值:对象,选项:对象):Promise

– newguy
5月17日12:44



#4 楼

@Bergi答案很好,但是我认为这不是最好的方法,因为您必须回到旧的then()方法,所以我认为更好的方法是捕获异步函数中的错误

async function someAsyncFunction(){
    const createdUser = await this.User.create(userInfo);

    console.log(createdUser)
}

someAsyncFunction().catch(console.log);



但是如果我们在同一函数中有许多await并需要捕获每个错误怎么办?

您可以声明to()函数

function to(promise) {
    return promise.then(data => {
        return [null, data];
    })
    .catch(err => [err]);
}



然后

async function someAsyncFunction(){
    let err, createdUser, anotherUser;

    [err, createdUser] = await to(this.User.create(userInfo));

    if (err) console.log(`Error is ${err}`);
    else console.log(`createdUser is ${createdUser}`);


    [err, anotherUser] = await to(this.User.create(anotherUserInfo));

    if (err) console.log(`Error is ${err}`);
    else console.log(`anotherUser is ${anotherUser}`);
}

someAsyncFunction();



阅读此内容时:“等待this.User.create” 。


最后,您可以创建模块“ to.js”,也可以简单地使用await-to-js模块。

您可以获得有关to的更多信息这篇文章中的功能

评论


那并不比等待更糟糕,因为它变老了。只是有所不同,并且适用于其他事物。另一方面,这种“等待(…)样式”使人想起了Nodeback样式的所有缺点。

–贝尔吉
19年2月25日在19:54

顺便说一句,为了获得更好的性能和简单性,您应该使用promise.then(data => [null,data],err => [err,null]);

–贝尔吉
19年2月25日在20:02

确切地说,“它只是有所不同,并且适用于其他事物” await用于创建具有“同步”之类的语法的代码,那么使用then和它的回调是更异步的语法。顺便说一句,感谢您的代码简化建议:)

–伊凡
19年2月27日在4:07