在 try 块中赋值的最佳方法

问题描述 投票:0回答:6
let x;
try {
  x = ...;
} catch (e) { 
  return
}

// rest of the code that uses `x`
const y = x + ...;

x
只分配一次,但我必须使用
let
而不是
const

另一种方式是:

try {
  const x = ...;
  // rest of the code that uses `x`
  const y = x + ...;
} catch (e) { 
  return
}

但是,这会增加嵌套并导致不清楚什么会引发错误。

有更好的方法吗?

如果

x
失败,我不必关心
try
的值,因为我将在
catch
区块中返回。
我也不想将它提取到单独的函数中。

javascript ecmascript-6 functional-programming
6个回答
1
投票

每当我遇到这样的事情时,我都会使用一个函数:

function constTryCatch(valueFn, catchFn) {
  try {
    return valueFn();
  } catch (e) {
    if (catchFn) catchFn(e);
    return null;
  }
}

const obj = { foo: 'bar' };
const x = constTryCatch(() => obj.foo);
console.log(x);
const y = constTryCatch(() => obj.foo.bar.baz, (e) => console.log(e));
console.log(y);
// example, if the rest of the block depends on `y` being truthy:
// if (!y) return;

注意,堆栈片段无法正确显示错误。在真正的浏览器控制台中,你会看到类似这样的内容:

酒吧

TypeError:无法读取未定义的属性“baz” 在 constTryCatch ((索引):79) 在 constTryCatch ((索引):69) 在 window.onload ((index):79)


1
投票

使用函数式 - 使用具有三个回调的辅助函数:

function Try(attempt, onSuccess, onFailure) {
  try {
    var res = attempt();
  } catch(err) {
    return onFailure(err);
  }
  return onSuccess(res);
}

这可以让你写

return Try(() => …, x => {
  // rest of the code that uses `x`
  const y = x + …;
}, e => void e);

您还可以使用表示此控制流的数据结构,例如

Result
monad(也称为
Either
monad):

class Result {
  constructor(go) {
    this.go = go;
  }
  static Ok(v) {
    return new this((onSuccess, _) => onSuccess(v));
  }
  static Err(r) {
    return new this((_, onFailure) => onFailure(v));
  }
  map(f) {
    return this.go(v => Result.Ok(f(v)), r => Result.Err(r));
  }
  chain(f) {
    return this.go(v => f(v), r => Result.Err(r));
  }
  unwrap() {
    return this.go(v => v, r => { throw r; });
  }
}
function Try(attempt) {
  try {
    var res = attempt();
    return Result.Ok(res);
  } catch(e) {
    return Result.Err(e);
  }
}

您可以像上面的简单辅助函数一样使用它:

return Try(() =>
  … // exceptions in here are caught
).go(x => {
  // rest of the code that uses `x` - exceptions are not caught
  const y = x + …;
}, e => void e);

而且还有更高级的链接:

return Try(() =>
  … // exceptions in here are caught
).chain(x =>
  Try(() =>
    x + … // exceptions in here are caught as well
  )
).map(y =>
  … // exceptions in here are not caught
).unwrap(); // any caught exceptions are re-thrown

0
投票

我会提取简单的函数来进行逻辑和错误处理:

function getXOrHandleError() {
  try {
    return ...
  } catch (e) {
    // handle error
    return
  }
}

const x = getXOrHandleError()
// ...

0
投票

使用一些功能标准库,例如 SanctuaryJS,您可以使用

Either
monad 重写代码,如下所示:

// encaseEither wraps a function so it handles a throw and
// gets the Error object and builds a Left value.
const parseJson = S.encaseEither ( S.I ) ( JSON.parse )

// This will end up in Right, because given JSON
// could be parsed.
const eitherJson = parseJson ( '[ 1, 2, 3 ]' )

// However, this will end up in Left, because given input
// isn't a valid JSON
const eitherJson2 = parseJson ( '{' )

// Sets [ 1, 2, 3 ]
const result = S.fromEither ( [] ) ( eitherJson )

// Sets an empty array
const result2 = S.fromEither ( [] ) ( eitherJson2 )

顺便说一句,如果你调用自己的函数,你应该尽可能避免

throw
并使用
Maybe
Either
:

const someFun = x => x < 5 ? S.Nothing : S.Just ( x * 2 )

// Sets Just ( 20 )
const maybeResult = someFun ( 10 )

// Sets  Nothing
const maybeResult2 = someFun ( 3 ) 

const sumFun2 = x => x * 3

// Just ( 30 )
const maybeResult3 = S.map ( somFun2 ) ( maybeResult )

// Nothing
const maybeResult4 = S.map ( someFun2 ) ( maybeResult2 )

// Get maybeResult3 or output a default
// So it sets 30
const result3 = S.fromMaybe ( 0 ) ( maybeResult3 )

// Get maybeResult3 or output a default
// So it sets 0
const result4 = S.fromMaybe ( 0 ) ( maybeResult4 )

我会完全避免例外。对于来自库和框架的那些函数,我会encase它们全部以使用 monad 来表达效果。


0
投票

我今天也遇到了同样的问题。我不喜欢创建新的实用函数来解决它的想法。太多的抽象会让你的团队成员难以理解代码。

我确实想到了另一种可能性,可以防止意外重新分配

x
(但这非常令人讨厌)。

let maybeX;
try {
  maybeX = ...;
} catch (e) {
  // do error handling 
  return;
}
const x = maybeX;

// rest of the code that uses `x`
const y = x + ...;

-1
投票

执行此操作的方式取决于如何处理错误。如果应该以不同的方式处理它们并且可以有条件地处理它们,则可以有一个

try...catch
{

try {
  const x = ...;
  // rest of the code that uses `x`
  const y = x + ...;
  ...
} catch (e) { 
  if (e ...)
    return;
  else if (e ...)
    ...
  else
    ...
}

或者,可以在调用函数中处理错误。

如果不能有条件地处理错误,那么应该有几个

try...catch

let x;
try {
  x = ...;
} catch (e) { 
  return;
}

// rest of the code that uses `x`
try {
  const y = x + ...;
  ...
} catch (e) { 
  ...
}

由于

x
是块作用域变量并且需要在另一个作用域中使用,因此需要在父作用域中使用
let
进行声明。

另一种方法是嵌套

try...catch
,这可以防止
x
的多次分配,但会增加嵌套级别:

try {
  const x = ...;

  // rest of the code that uses `x`
  try {
   const y = x + ...;
    ...
  } catch (e) { 
    ...
  }
} catch (e) { 
  return;
}
© www.soinside.com 2019 - 2024. All rights reserved.