Вы можете предоставить себе служебные функции для отчетов об ошибках и обработчиков событий, например:
function handleError(err) {
if (!(err instanceof Error)) {
err = Error(err);
}
output("error handler: " + err.message, "yellow");
}
function wrapHandler(fn) {
return function(evt) {
new Promise(resolve => {
resolve(fn(evt));
}).catch(e => {
handleError(e);
});
};
}
, который поддерживает как async
, так и не async
обработчики событий. Если есть синхронная ошибка, вызывающая fn
, она перехватывается конструктором обещания и превращается в отклонение создаваемого обещания. Если нет, обещание преобразуется в возвращаемое значение fn
, что означает, что если fn
возвращает обещание, которое отклоняет, обещание, созданное new Promise
, отклоняется. Так или иначе, ошибки go в обработчике ошибок.
Я не пытался различить guish между ошибками и отклонениями, поскольку они в основном одно и то же, но вы могли бы, если бы Вы хотите:
function handleError(err, isRejection) {
if (!(err instanceof Error)) {
err = Error(err);
}
output("error handler: " + err.message, isRejection ? "green" : "yellow");
}
function wrapHandler(fn) {
return function(evt) {
try {
const result = fn(event);
Promise.resolve(result).catch(e => handleError(e, true));
} catch (e) {
handleError(e, false);
}
};
}
В любом случае, вы бы настроили глобальные обработчики для его использования и запретили бы использование по умолчанию:
window.addEventListener("error", errorEvent => {
handleError(errorEvent.error, false); // Remove the `, false` if you're not trying to make a distinction
errorEvent.preventDefault();
});
window.addEventListener("unhandledrejection", errorEvent => {
handleError(errorEvent.reason, true); // Remove the `, true` if you're not trying to make a distinction
errorEvent.preventDefault();
});
Вы использовали бы wrapHandler
при настройке ваши обработчики, либо напрямую:
btn.addEventListener("click", wrapHandler(async evt => {
evt.stopPropagation();
output("The button was clicked");
noFunction(); // FIXME:
}));
... или с помощью другой служебной функции:
function addListener(elm, eventName, fn) {
const handler = wrapHandler(fn);
return elm.addEventListener(eventName, handler);
return function() {
elm.removeEventListener(handler);
};
}
... then:
const removeBtnClick = addListener(btn, "click", async evt => {
evt.stopPropagation();
output("The button was clicked");
noFunction(); // FIXME:
});
// ...if you want to remove it later...
removeBtnClick();
Live Example - поскольку ваш оригинал различал синхронные ошибки и отклонения, я использовал этот вариант здесь, но опять-таки, это «действительно различие без разницы, и я бы не стал различать guish их в моем собственном коде:
function handleError(err, isRejection) {
if (!(err instanceof Error)) {
err = Error(err);
}
output("error handler: " + err.message, isRejection ? "green" : "yellow");
}
window.addEventListener("error", errorEvent => {
handleError(errorEvent.error, false);
errorEvent.preventDefault();
});
window.addEventListener("unhandledrejection", errorEvent => {
handleError(errorEvent.reason, true);
errorEvent.preventDefault();
});
function wrapHandler(fn) {
return function(evt) {
try {
const result = fn(event);
Promise.resolve(result).catch(e => handleError(e, true));
} catch (e) {
handleError(e, false);
}
};
}
function addListener(elm, eventName, fn) {
const handler = wrapHandler(fn);
return elm.addEventListener(eventName, handler);
return function() {
elm.removeEventListener(handler);
};
}
function output(txt, color) {
const div = document.createElement("p");
div.textContent = txt;
if (color) div.style.backgroundColor = color;
document.body.appendChild(div);
}
const btn = document.createElement("button");
btn.innerHTML = "The button";
addListener(btn, "click", async evt => {
evt.stopPropagation();
output("The button was clicked");
noFunction(); // FIXME:
});
document.body.appendChild(btn);
const btn2 = document.createElement("button");
btn2.innerHTML = "With try/catch";
addListener(btn2, "click", async evt => {
evt.stopPropagation();
try {
output("Button 2 was clicked");
noFunction2(); // FIXME:
} catch (err) {
console.warn("catch", err)
throw Error(err);
}
});
document.body.appendChild(btn2);
new Promise(function(resolve, reject) {
setTimeout(function() {
return reject('oh noes');
}, 100);
});
justAnError();
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1">