【问题标题】:How to properly implement error handling in async/await case如何在异步/等待情况下正确实现错误处理
【发布时间】:2018-07-19 01:05:55
【问题描述】:

我使用 async/await ecma6 标准,没有任何自定义库。

我目前不知道如何正确捕获和抛出错误。 我有多个 async/await 函数,如果发生严重错误以下的某个地方,我想将错误抛出到所有异步函数的顶部并停止执行该函数。

我试图从 async/await 函数中抛出异常并在目标函数中捕获它,但在 node.js 中出现错误:

    this.basicAuthLogin= async function(user)
{
    "use strict";
    const login = new Login(this.host, this.url, user, user.pw);

    //getSessionID throws error
    this.sessionID = getSessionID(result.request.response);
}

(node:13964) UnhandledPromiseRejectionWarning: 未处理的承诺 拒绝(拒绝 id:1):错误:getSessionID 响应未定义 (节点:13964)[DEP0018] 弃用警告:未处理的承诺 拒绝被弃用。在未来,承诺拒绝是 未处理将以非零退出终止 Node.js 进程 代码。已附加调试器。

所以看来我不允许从异步函数中抛出异常,甚至在 node.js 中的 promise 的 catch 块中重新抛出异常?

那么我该如何让它工作呢? 我是否应该在异步函数中捕获错误并在承诺中返回错误并重新抛出异步函数?

   this.basicAuthLogin= async function(user)
{
    "use strict";
    const login = new Login(this.host, this.url, user, user.pw);
   try{
    //getSessionID throws error
    this.sessionID = getSessionID(result.request.response);
   } catch(err) { return err;}
}

但这意味着在我的第一个异步函数的调用堆栈中,每个函数都需要异步,即使我并不真正需要它,我也必须等待它。

希望有人能赐教。

问候 鲁维

编辑基本调用堆栈伪代码:

   async startTest[arr]{

    for (var i = 0; i < arr.length; i++)
    {
      try {
          await runStep(arr[i];
        } catch(err) { 
            console.log(err);
            break; 
        }
      }
    }

  async runStep(step)
  {
     try {
     var userIsValid = await validateUser(step.user);
     var req = buildRequest(step.request);
     var result = await sendRequest(req);
     var verify = verifyResult();
     } catch(err){ throw err;}
  }

  async validateUser(user)
  {
     //make checks
     //
     var result = await this.authenticate(parameter).catch(err => {throw err});
     userFound = true;
   }

  function authenticate(parameter) {
  //can throw async function
   basicAuthLogin(parameter).catch(err => {throw err};

   }

  function async basicAuthLogin(parameter()
  {
   try {
    //can throw  async function
      var result = await request(parameter);
      //can throw sync function
      this.sessionID = getSessionID(response);
      //can throw   sync function
      } catch(err) { throw err; }
   }

【问题讨论】:

  • getSessionID 是异步的吗?是因为它是 async 还是因为它返回了一个承诺?
  • 不,它不是异步的,它是一个简单的函数,它抛出一个异常,我想在调用堆栈上捕获 5 或 6 层,但似乎我不允许这样做。
  • getSessionID response is undefined 我认为您应该确保响应已定义
  • @Ruvi:谢谢。既然basicAuthLogin 中没有任何东西是异步的,那为什么它是async 函数呢?
  • 请修正缩进以使示例更易于阅读,暂时投反对票。

标签: javascript node.js promise async-await


【解决方案1】:

async/await 的一大优点是它们使 try/catch 能够处理您的异步代码。

您的第一个basicAuthLogin 函数绝对没问题(前提是getSessionID 是一个同步 函数;如果不是,则您缺少await [你现在说是])。 使用 basicAuthLogin 的代码必须处理它抛出的可能性(通过处理错误或允许它传播给负责处理它的调用者)。所以要么:

// In an `async` function
try {
    await this.basicAuthLogin(/*...*/);
} catch (e) {
    // Handle the fact something failed
}

// NOT in an `async` function:
this.basicAuthLogin(/*...*/)
    .catch(e => { /* Handle the fact something failed */ });

如果使用它的代码执行这两件事之一(或让错误传播到执行这两件事之一的代码),您将不会收到“未处理的拒绝”错误。

在回复我询问getSessionID 是否异步的评论时,您写道:

不,它不是异步的,它是一个简单的函数,它抛出一个异常,我想在调用堆栈上捕获 5 或 6 层,但似乎我不允许这样做。

这是一个活生生的例子(在我的例子中,我已经让basicAuthLogingetSessionID 之前使用了一些异步的东西,但这并不重要):

const getSessionID = () => {
  throw new Error("Failed");
};
const somethingAsync = () => new Promise(resolve => {
  setTimeout(resolve, 100);
});
const basicAuthLogin = async function(user)
{
    "use strict";
    await somethingAsync();
    /*
    const login = new Login(this.host, this.url, user, user.pw);
    */

    //getSessionID throws error
    getSessionID();
};

const wrapper1 = async () => {
  await basicAuthLogin();
};
const wrapper2 = async () => {
  await wrapper1();
};
const wrapper3 = async () => {
  await wrapper2();
};

// Top-level caller
(async () => {
  try {
    await wrapper3();
  } catch (e) {
    console.log("Caught error: " + e.message);
  }
})();

规则就像它有例外一样(因为理论上这些例外):

  1. 要么处理它(例如,try/catch),要么让它传播给调用者(通常不做任何事情),并且

  2. 顶层必须处理它

规则#2 意味着当您从非async 代码转换到async 代码(通常位于堆栈顶部)时,您需要一个包装器。要么:

(async () => {
  try {
    await theFirstAsyncFunction();
    await theNextAsyncFunction();
    await aThirdAsyncFunction();
  } catch (e) {
    // handle the error
  }
})();

(async () => {
  await theFirstAsyncFunction();
  await theNextAsyncFunction();
  await aThirdAsyncFunction();
})().catch(e => { /* handle the error */});

当然也可以:

theFirstAsyncFunction()
.then(() => theNextAsyncFunction())
.then(() => aThirdAsyncFunction())
.catch(e => { /* handle the error */});

有一个共同点:顶层总是处理错误。

【讨论】:

  • 非常感谢您非常详细的回答。所以我有两个选项用包装器链接承诺结果,或者在每个级别重新抛出异常?因为最后一个似乎不适用于我添加的代码示例。
  • @Ruvi:不,根本没有必要在每个级别都重新抛出,这对于async/await 来说是一个非常糟糕的设计。看看答案中的可运行示例:wrapper1wrapper2wrapper3basicAuthLogin 都没有被错误处理困扰,他们只是做他们的工作;来自getSessionID 的错误会自动通过它们传播,就像非async 代码一样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-11-12
  • 2018-02-13
  • 1970-01-01
  • 2018-07-29
  • 2018-07-26
  • 2018-11-12
  • 2022-01-09
相关资源
最近更新 更多