admin管理员组

文章数量:1191752

A great pattern with ES2017 async/await is:

async function () {
  try {
    var result = await some_promised_value()
  } catch (err) {
    console.log(`This block would be processed in
      a reject() callback with promise patterns
      but this is far more intuitive`)
    return false // or something less obtuse
  }
  result = do_something_to_result(result)
  return result;
}

Being able to handle errors like that is really nice. But say I want to asynchronously get a value I'd like to protect it from reassignment (eg: a database session), but I still want to use the async/await pattern (purely because I think it's far more intuitive).

The following won't work because const is block scoped:

async function () {
  try {
    const result = await get_session()
  } catch (err) {
    console.log(`This block should catch any
      instantiation errors.`)
    return false
  }
  // Can't get at result here because it is block scoped.
}

Of course you could execute the rest of the function within the try block, but then you risk errors getting caught that should be let fall through somewhere else. (For example I hit this challenge writing tests where errors like test failures need to fall back to the test suite but wanted to handle driver instantiation myself. Perhaps I'm falling into some kind of anti-pattern here by not letting those errors fall back to the test suite.)

The simple answer obviously is don't use that pattern here. Use Promises and callbacks instead. Or use a var and avoid block scoped const and let. But I like the benefits of reassignment protection and block scope for other considerations.

[question]: Is there a way to wrap an await/async try/catch block to every function? Seems to be one potential solution, but doesn't quite come up as readable as try/catch. The fact that try/catch doesn't break function scope and I can use return from within a try/catch block seems to me to be more in line with the spirit of async/await bringing a more procedural-looking logic to asynchronous code.

Ideally you'd want to do something like const x = await y() catch (err) or const x = await y() || fail But I can't think of anything with a similar flow that's syntactically correct.

UPDATE: As suggested by @jmar777 below another alternative is:

const x = await y().catch(() => {/*handle errors here*/})

Which is probably the closest I've found to those last two examples in practice. But it breaks the return scope that blocks downstream execution in the examples above. Which is a nice synchronous-like way of handling things.

Each solution has its trade offs.

I've manually hoisted the variables with let at the top of the function block as a working solution (as described in jmar777's answer) still an interesting question to see the various approaches, so I'll leave the question open for now.

A great pattern with ES2017 async/await is:

async function () {
  try {
    var result = await some_promised_value()
  } catch (err) {
    console.log(`This block would be processed in
      a reject() callback with promise patterns
      but this is far more intuitive`)
    return false // or something less obtuse
  }
  result = do_something_to_result(result)
  return result;
}

Being able to handle errors like that is really nice. But say I want to asynchronously get a value I'd like to protect it from reassignment (eg: a database session), but I still want to use the async/await pattern (purely because I think it's far more intuitive).

The following won't work because const is block scoped:

async function () {
  try {
    const result = await get_session()
  } catch (err) {
    console.log(`This block should catch any
      instantiation errors.`)
    return false
  }
  // Can't get at result here because it is block scoped.
}

Of course you could execute the rest of the function within the try block, but then you risk errors getting caught that should be let fall through somewhere else. (For example I hit this challenge writing tests where errors like test failures need to fall back to the test suite but wanted to handle driver instantiation myself. Perhaps I'm falling into some kind of anti-pattern here by not letting those errors fall back to the test suite.)

The simple answer obviously is don't use that pattern here. Use Promises and callbacks instead. Or use a var and avoid block scoped const and let. But I like the benefits of reassignment protection and block scope for other considerations.

[question]: Is there a way to wrap an await/async try/catch block to every function? Seems to be one potential solution, but doesn't quite come up as readable as try/catch. The fact that try/catch doesn't break function scope and I can use return from within a try/catch block seems to me to be more in line with the spirit of async/await bringing a more procedural-looking logic to asynchronous code.

Ideally you'd want to do something like const x = await y() catch (err) or const x = await y() || fail But I can't think of anything with a similar flow that's syntactically correct.

UPDATE: As suggested by @jmar777 below another alternative is:

const x = await y().catch(() => {/*handle errors here*/})

Which is probably the closest I've found to those last two examples in practice. But it breaks the return scope that blocks downstream execution in the examples above. Which is a nice synchronous-like way of handling things.

Each solution has its trade offs.

I've manually hoisted the variables with let at the top of the function block as a working solution (as described in jmar777's answer) still an interesting question to see the various approaches, so I'll leave the question open for now.

Share Improve this question edited May 23, 2017 at 12:33 CommunityBot 11 silver badge asked Jan 18, 2017 at 5:00 Tim HopeTim Hope 7941 gold badge7 silver badges17 bronze badges 3
  • 1 async/await is not part of ES7 (ES2016). It's going to be part of this year's release, ES2017. – Felix Kling Commented Jan 18, 2017 at 5:02
  • A similar answer here hope can help you. – Cooper Hsiung Commented Mar 12, 2018 at 9:09
  • See also syntax to handle rejections with async/await – Bergi Commented Jun 15, 2020 at 18:51
Add a comment  | 

2 Answers 2

Reset to default 16

You may have a misunderstanding regarding the benefits of using const. Assigning a value using a const declaration doesn't make that value immutable, it simply means that the value of that constant can't be changed or redeclared:

The const declaration creates a read-only reference to a value. It does not mean the value it holds is immutable, just that the variable identifier cannot be reassigned. For instance, in case the content is an object, this means the object itself can still be altered. (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const)

In your example, you're probably best off manually hoisting your result binding outside of the try/catch:

async function () {
  let result;

  try {
    result = await get_session()
  } catch (err) {
    console.log(`This block should catch any
      instantiation errors.`)
  }

  // do whatever else you need with result here...
}

An alternative would be to restructure the code such that you don't need to rely on a try/catch at all. For example:

async function () {
  const result = await get_session().catch(someRejectionHandler); 

  // do whatever else you need with result here...
}

Just be aware that your downstream code needs to gracefully handle the case where get_session() was rejected, and result isn't initialized based on a successful response. This is no different than in your initial example, but perhaps not quite as obvious when scanning the code.

Coming back to this question a few years later a far simpler answer has occurred to me, I don’t see in retrospect why I was interested in a small try/catch pair and then performing more logic outside of it when I could simply perform all the logic and return inside the try statement.

Taking the example from the question and applying that structure it would be:

 async function () {
  try {
    const result = await get_session()
    // Do what needs to be done and then:
    return result
  } catch (err) {
    console.log(`This block should catch any
      instantiation errors.`)
    return false
  }
}

If you need to access anything from the try block in the catch block raise it in an error. There may be a reason I needed to move out of the try scope when I asked this question, but I can’t see a scenario that would necessitate it looking back. This solution wouldn’t work if you were to use try/catch/finally and return in finally. But I don’t think I’ve come across that pattern much at all in the wild.

本文标签: nodejsElegantly Handling Reject on awaited Javascript PromiseStack Overflow