有时需要调用函数并直接处理异常(使用 try/catch),而其他时候最好调用 same 函数并接收
null
或 false
如果它会否则失败。
这个想法或用例类似于 ActiveRecord 中的 Rails,您可以选择调用
User.find(1)
或 User.find!(1)
——如果找不到,前者将返回 nil
,而后者将引发异常。
问题: 我是这些功能的作者,所以我可以以任何方式设计它。这个想法在 javascript 中有规定的模式吗?
一个想法是模仿 ActiveRecord 中所做的事情:
// returns API data or bubbles-up exception
function getDataFromAPI() {
const response = callApi(); // function throws error if not 200
return response.data;
}
// returns API data or null
function getDataFromAPI_() {
try {
return getDataFromAPI();
} catch(e) {
return null;
}
}
也许另一种方法是为 try/catch 创建一个通用包装器,尽管这个例子没有考虑参数:
// returns API data, but will raise an exception if
// `response` has no `data` property...
function getDataFromAPI() {
const response = callApi();
return response.data;
}
// return function or null
function nullIfError(func) {
try {
return func();
} catch(e) {
return null;
}
}
nullIfError(getDataFromAPI)
或者,添加一个参数以有条件地改变它的行为,但我不喜欢它总是包含在 try/catch 中......
function getDataFromAPI(throwError = true) {
try {
const response = callApi();
return response.data;
} catch(e) {
if (throwError) throw(e);
else return null;
}
}
即使没有真正建立的模式,也可以引入原型和/或非原型实现可能需要的函数/方法修饰符(例如
afterFinally
、afterThrowing
、after
、before
、around
) 允许在此类修改后的函数/方法的调用/应用/执行时间根据其参数、返回值和上下文来拦截、自省和操作函数/方法的控制流。
afterThrowing
修饰符 非常方便。
下一个提供的示例代码显示了它的实现和用法。
function deliverData() {
return { data: { foo: "bar "} };
}
function throwError() {
throw new Error('API not available.');
}
function callMockedApi() {
return [deliverData, throwError][Math.floor(Math.random() * 2)]();
}
function getDataFromAPI() {
const response = callMockedApi();
return response.data;
}
const getDataFromAPI_ = getDataFromAPI.afterThrowing(() => null);
console.log(getDataFromAPI_());
console.log(getDataFromAPI());
<script>
function isFunction(value) {
return (
'function' === typeof value &&
'function' === typeof value.call &&
'function' === typeof value.apply
);
}
function getSanitizedTarget(value) {
return value ?? null;
}
function afterThrowing(handler, target) {
target = getSanitizedTarget(target);
const proceed = this;
return (
isFunction(handler) &&
isFunction(proceed) &&
function afterThrowingType(...argumentArray) {
const context = getSanitizedTarget(this) ?? target;
let result;
try {
result = proceed.apply(context, argumentArray);
} catch (exception) {
result = handler.call(context, exception, argumentArray);
}
return result;
}
) || proceed;
}
Reflect.defineProperty(Function.prototype, 'afterThrowing', {
configurable: true,
writable: true,
value: afterThrowing,
});
</script>
有关此主题的更多信息,请阅读 ...