正确的 Try...Catch 语法使用 Async/Await

IT技术 javascript promise async-await try-catch ecmascript-2017
2021-01-31 14:40:39

我喜欢Async/AwaitTypescript 等中可用的新功能的平坦性。但是,我不确定我是否喜欢这样一个事实,即我必须awaittry...catch的外部声明我ing的变量以便以后使用它。像这样:

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

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

如果我错了,请纠正我,但最好不要try正文中放置多行业务逻辑,所以我只能选择createdUser在块外声明,在块中分配它,以及然后使用它。

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

4个回答

最好的做法似乎是不要在 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(仍然)没有对条件异常的语法支持

    如果您的方法没有返回因足够具体的错误而被拒绝的Promise,您可以通过在.catch()处理程序中重新抛出更合适的内容来自己做到这一点

      try {
          const createdUser = await this.User.create(userInfo).catch(err => {
              throw new CreationError(err.message, {code: "USER_CREATE"});
          });
          …
      } …
    

    另请参阅处理Promise链中的多个捕获以了解此之前的async/await版本。

  • then与两个回调一起使用而不是try/ catch这确实是最不丑陋的方式,我个人的建议也是因为它的简单性和正确性,不依赖标记的错误或结果值的外观来区分履行和拒绝Promise:

      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从外部函数执行 early s。

.then()调用返回的 lambda 表达式是否需要 async 关键字?
2021-03-18 14:40:39
我见过有人直接将 catch 处理程序附加到 await 。这样做或将其包装在 try/catch 中是个好主意吗?
2021-03-22 14:40:39
@Sarojconst result = await something().catch(err => fallback);比是的更简单let result; try { result = await something(); } catch(err) { result = fallback; },在那种情况下,我认为这是一个好主意。
2021-03-25 14:40:39
您的最后一个示例用于.then()解决Promise并提供回调,因此可能await在那里无效。
2021-04-05 14:40:39
@dcorking 这是调用await返回的Promise.then(…)
2021-04-05 14:40:39

另一种更简单的方法是将 .catch 附加到 promise 函数。前任:

const createdUser = await this.User.create(userInfo).catch( error => {
// handle error
})
我从来没有想过这个,但是我试了一下,它有一个有趣的副作用:你可以return.catch()回调中设置一个结果值。否则,它返回未定义。
2021-03-24 14:40:39

我通常使用 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 }));
};

然后按如下方式使用它:

const createdUser = await this.User.create(userInfo).catchToObj();
if (createdUser && createdUser.error) {
    console.error(createdUser.error);
}
我正在使用 Sequelize 的create方法,它返回一个Promise<Model>. 定义是:public static async create(values: object, options: object): Promise<Model>
2021-03-12 14:40:39
我使用最后一种方法,它给了我'catchToObj' is not a function错误。
2021-03-14 14:40:39
在您调用我的答案中的第一个代码段后,@newguycatchToObj将存在于每个Promise对象如果您的函数没有返回 aPromise它将不起作用
2021-04-07 14:40:39

@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”。

最后,您可以创建module“to.js”或简单地使用await-to-jsmodule。

你可以to这篇文章中获得更多关于函数的信息

顺便说一句,为了更好的性能和简单性,您应该使用 promise.then(data => [null, data], err => [err, null]);
2021-04-02 14:40:39
正是“它只是不同,适合其他事物”await用于创建具有“同步”类似语法的代码,使用then它的回调是更异步的语法。顺便说一句,感谢您的代码简单性建议:)
2021-04-04 14:40:39
then不比await因为它更糟它只是不同,适用于其他事物。await to(…)另一方面,这种“风格”让人想起 nodeback 风格及其所有缺点。
2021-04-09 14:40:39