使用async/await尝试/捕获块

Pat*_*ick 100 node.js async-await ecmascript-2017

我正在深入研究节点7 async/await功能,并在这样的代码中保持绊脚石

function getQuote() {
  let quote = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
  return quote;
}

async function main() {
  try {
    var quote = await getQuote();
    console.log(quote);
  } catch (error) {
    console.error(error);
  }
}

main();
Run Code Online (Sandbox Code Playgroud)

这似乎是使用async/await解析/拒绝或返回/抛出的唯一可能,但是,v8不会优化try/catch块中的代码?!

还有替代品吗?

rsp*_*rsp 117

备择方案

替代方案:

async function main() {
  try {
    var quote = await getQuote();
    console.log(quote);
  } catch (error) {
    console.error(error);
  }
}
Run Code Online (Sandbox Code Playgroud)

会是这样的,明确地使用promises:

function main() {
  getQuote().then((quote) => {
    console.log(quote);
  }).catch((error) => {
    console.error(error);
  });
}
Run Code Online (Sandbox Code Playgroud)

或类似的东西,使用延续传递方式:

function main() {
  getQuote((error, quote) => {
    if (error) {
      console.error(error);
    } else {
      console.log(quote);
    }
  });
}
Run Code Online (Sandbox Code Playgroud)

原始的例子

您的原始代码所做的是暂停执行并等待返回的承诺getQuote()来解决.然后它继续执行并将返回的值写入var quote,然后在promise被解析时打印它,或者抛出异常并运行catch块,如果promise被拒绝则打印错误.

您可以像使用第二个示例一样直接使用Promise API执行相同的操作.

性能

现在,为了表现.我们来试试吧!

我刚写了这段代码 - 作为返回值f1()给出1,f2()抛出1异常:

function f1() {
  return 1;
}

function f2() {
  throw 1;
}
Run Code Online (Sandbox Code Playgroud)

现在让我们拨打相同的代码数百万次,首先是f1():

var sum = 0;
for (var i = 0; i < 1e6; i++) {
  try {
    sum += f1();
  } catch (e) {
    sum += e;
  }
}
console.log(sum);
Run Code Online (Sandbox Code Playgroud)

然后让我们f1()改为f2():

var sum = 0;
for (var i = 0; i < 1e6; i++) {
  try {
    sum += f2();
  } catch (e) {
    sum += e;
  }
}
console.log(sum);
Run Code Online (Sandbox Code Playgroud)

这是我得到的结果f1:

$ time node throw-test.js 
1000000

real    0m0.073s
user    0m0.070s
sys     0m0.004s
Run Code Online (Sandbox Code Playgroud)

这就是我得到的f2:

$ time node throw-test.js 
1000000

real    0m0.632s
user    0m0.629s
sys     0m0.004s
Run Code Online (Sandbox Code Playgroud)

看起来你可以在一个单线程进程中做一两次抛出一秒钟的事情.如果你做的不止于此,你可能需要担心它.

摘要

我不担心Node中的那些东西.如果这样的事情得到了很多使用,那么它最终将由V8或SpiderMonkey或Chakra团队进行优化,每个人都会关注 - 它并不像它没有被优化为原则,它只是不是问题.

即使它没有经过优化,我仍然会争辩说如果你在Node中最大化你的CPU那么你应该用C编写你的数字运算 - 这就是本机插件的用途,等等.或者像node.native这样的东西比Node.js更适合这个工作.

我想知道什么是需要抛出这么多异常的用例.通常抛出异常而不是返回值是一个例外.

  • @Patrick"可能是"和"将会"是推测和实际测试之间的差异.我测试了它的单个语句,因为这是你的问题,但你可以轻松地转换我的例子来测试多个语句.我还提供了几个其他选项来编写您也询问过的异步代码.如果它回答了您的问题,那么您可以考虑[接受答案](https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work/5235#5235).总结一下:当然,异常比回报慢,但它们的用法应该是一个例外. (4认同)
  • 抛出异常确实应该是异常。话虽如此,无论您是否抛出异常,代码都未优化。性能损失来自使用“try catch”,而不是抛出异常。虽然数字很小,但根据您的测试,速度几乎慢了 10 倍,这并不是微不足道的。 (2认同)

Pul*_*dha 16

try-catch块的替代方法是await-to-js lib.我经常使用它.例如:

import to from 'await-to-js';

async function main(callback) {
    const [err,quote] = await to(getQuote());

    if(err || !quote) return callback(new Error('No Quote found');

    callback(null,quote);

}
Run Code Online (Sandbox Code Playgroud)

与try-catch相比,此语法更清晰.

  • 您甚至不需要安装库。如果你查看它的源代码,它实际上是 1 个函数。只需将该函数复制并粘贴到项目中的实用程序文件中即可。 (2认同)

小智 15

async function main() {
  var getQuoteError
  var quote = await getQuote().catch(err => { getQuoteError = err }

  if (getQuoteError) return console.error(err)

  console.log(quote)
}
Run Code Online (Sandbox Code Playgroud)

或者,您可以执行以下操作,而不是声明可能的var以在顶部保存错误

if (quote instanceof Error) {
  // ...
}
Run Code Online (Sandbox Code Playgroud)

虽然如果抛出类似TypeError或Reference错误的东西,这将无效.您可以确保它是一个常规错误

async function main() {
  var quote = await getQuote().catch(err => {
    console.error(err)      

    return new Error('Error getting quote')
  })

  if (quote instanceOf Error) return quote // get out of here or do whatever

  console.log(quote)
}
Run Code Online (Sandbox Code Playgroud)

我对此的偏好是将所有内容包装在一个大的try-catch块中,其中创建了多个promise可能会使得处理特定于创建它的promise的错误变得很麻烦.替代方案是多个try-catch块,我觉得同样麻烦


Ste*_*ton 13

替代类似于Golang中的错误处理

因为async/await使用了底层的promises,你可以编写一个像这样的小实用函数:

export function catchEm(promise) {
  return promise.then(data => [null, data])
    .catch(err => [err]);
}
Run Code Online (Sandbox Code Playgroud)

然后在需要捕获一些错误时导入它,并包装异步函数,该函数返回一个promise.

import catchEm from 'utility';

async performAsyncWork() {
  const [err, data] = await catchEm(asyncFunction(arg1, arg2));
  if (err) {
    // handle errors
  } else {
    // use data
  }
}
Run Code Online (Sandbox Code Playgroud)

  • @Mike 您可能正在重新发明轮子 - 已经有一个流行的软件包可以做到这一点:https://www.npmjs.com/package/await-to-js (3认同)
  • golang 不是节点。 (2认同)

zar*_*ior 12

更清洁的替代方案如下:

由于每个异步函数在技术上都是一个承诺

您可以在使用 await 调用函数时向函数添加捕获

async function a(){
    let error;

    // log the error on the parent
    await b().catch((err)=>console.log('b.failed'))

    // change an error variable
    await c().catch((err)=>{error=true; console.log(err)})

    // return whatever you want
    return error ? d() : null;
}
a().catch(()=>console.log('main program failed'))
Run Code Online (Sandbox Code Playgroud)

不需要try catch,因为所有的promise错误都被处理了,而且你没有代码错误,你可以在父级中省略它!!

假设您正在使用 mongodb,如果出现错误,您可能更喜欢在调用它的函数中处理它,而不是制作包装器或使用 try 捕获。

  • 该问题并不要求在承诺失败后执行。在这里,您等待 B,然后运行 ​​C,如果出错则返回 D。这个清洁工怎么样?C必须等待B,但他们彼此独立。如果他们是独立的,我不明白为什么他们会在一起。如果它们相互依赖,那么如果 B 失败,您会希望停止 C 的执行,即 .then.catch 或 try-catch 的工作。我假设它们不返回任何内容,并执行一些与 A 完全无关的异步操作。为什么用 async wait 调用它们? (2认同)