使用async / await尝试/捕获块

时间:2016-11-30 09:06:20

标签: node.js async-await ecmascript-2017

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

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

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

有替代方案吗?

7 个答案:

答案 0 :(得分:95)

替代

替代方案:

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

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

function main() {
  getQuote().then((quote) => {
    console.log(quote);
  }).catch((error) => {
    console.error(error);
  });
}

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

function main() {
  getQuote((error, quote) => {
    if (error) {
      console.error(error);
    } else {
      console.log(quote);
    }
  });
}

原始示例

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

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

效果

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

我刚刚编写了此代码 - f1()1作为返回值,f2()抛出1作为例外:

function f1() {
  return 1;
}

function f2() {
  throw 1;
}

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

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

然后让f1()更改为f2()

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

这是我为f1得到的结果:

$ time node throw-test.js 
1000000

real    0m0.073s
user    0m0.070s
sys     0m0.004s

这是我为f2获得的:

$ time node throw-test.js 
1000000

real    0m0.632s
user    0m0.629s
sys     0m0.004s

在一个单线程进程中,您似乎可以在一秒钟内完成200万次抛出。如果你做的不止于此,你可能需要担心它。

摘要

我不担心Node中的那些事情。如果这样的事情被大量使用,那么它最终将由V8或SpiderMonkey或Chakra团队进行优化,每个人都会遵循 - 它并不像它没有被优化为原则,它是不是问题。

即使它没有经过优化,我仍然认为如果你在Node中最大化你的CPU那么你应该用C编写你的数字运算 - 这是什么除其他外,原生插件用于。或者像node.native这样的东西比Node.js更适合这项工作。

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

答案 1 :(得分:12)

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

logChanges

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

答案 2 :(得分:10)

['abcddd', 'bcddd', 'cddd', 'ddd', 'dd', 'd']
['abc', 'bcd', 'cdd', 'ddd']

或者,不是声明可能的var来保持顶部的错误,而是

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

  if (getQuoteError) return console.error(err)

  console.log(quote)
}

虽然如果抛出TypeError或Reference错误之类的东西,这将无效。您可以通过

确保它是一个常规错误
if (quote instanceof Error) {
  // ...
}

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

答案 3 :(得分:10)

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

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

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

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

import catchEm from 'utility';

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

答案 4 :(得分:1)

我想这样做:)

const sthError = () => Promise.reject('sth error');

const test = opts => {
  return (async () => {

    // do sth
    await sthError();
    return 'ok';

  })().catch(err => {
    console.error(err); // error will be catched there 
  });
};

test().then(ret => {
  console.log(ret);
});

与使用co

处理错误类似
const test = opts => {
  return co(function*() {

    // do sth
    yield sthError();
    return 'ok';

  }).catch(err => {
    console.error(err);
  });
};

答案 5 :(得分:1)

更清洁的替代方法如下:

由于每个异步功能从技术上来说都是一个承诺

您可以在通过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'))

无需尝试捕获,因为已处理所有承诺错误,并且您没有代码错误,因此可以在父级中忽略它!

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

答案 6 :(得分:0)

根据我的经验,以这种方式

catch是危险的。整个堆栈中抛出的任何错误都会被捕获,而不仅仅是这个承诺中的错误(可能不是您想要的)。

promise的第二个参数已经是拒绝/失败回调。改用它会更好,更安全。

这是我为处理此问题而写的一种打字稿类型安全的单行代码:

function wait<R, E>(promise: Promise<R>): [R | null, E | null] {
  return (promise.then((data: R) => [data, null], (err: E) => [null, err]) as any) as [R, E];
}

// Usage
const [currUser, currUserError] = await wait<GetCurrentUser_user, GetCurrentUser_errors>(
  apiClient.getCurrentUser()
);
相关问题