草庐IT

javascript - 如何在异步/等待情况下正确实现错误处理

coder 2024-07-27 原文

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

我现在不知道如何才能正确捕获和抛出错误。 我有多个异步/等待函数,如果低于严重错误的某处发生我想将错误抛出到所有异步函数的顶部并停止执行该函数。

我试图从 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: Unhandled promise rejection (rejection id: 1): Error: getSessionID response is undefined (node:13964) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. Debugger attached.

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

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

   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;}
}

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

希望有人能赐教。

问候 鲁维

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

   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; }
   }

最佳答案

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 是否异步的评论,您写道:

No it is not async it is a simple function that throws an exception that I want to catch 5 or 6 floors up the call stack but it seems I am not allowed to do that.

这是一个实际的例子(在我的例子中,我已经让 basicAuthLogin 实际上在 getSessionID 之前使用一些异步的东西,但是这并不重要):

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);
  }
})();

规则就像异常(exception)一样(因为理论上这些异常(exception)):

  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 */});

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

关于javascript - 如何在异步/等待情况下正确实现错误处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48685872/

有关javascript - 如何在异步/等待情况下正确实现错误处理的更多相关文章

  1. ruby - 如何在 Ruby 中顺序创建 PI - 2

    出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

  2. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  3. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  4. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  5. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  6. ruby - 默认情况下使选项为 false - 2

    这是在Ruby中设置默认值的常用方法:classQuietByDefaultdefinitialize(opts={})@verbose=opts[:verbose]endend这是一个容易落入的陷阱:classVerboseNoMatterWhatdefinitialize(opts={})@verbose=opts[:verbose]||trueendend正确的做法是:classVerboseByDefaultdefinitialize(opts={})@verbose=opts.include?(:verbose)?opts[:verbose]:trueendend编写Verb

  7. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  8. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  9. ruby - 如何在 Ruby 中拆分参数字符串 Bash 样式? - 2

    我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"

  10. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

随机推荐