Comments 24
И да, толку от обработки вида `console.error(err)` очень мало. Может, вовсе не стоит ловить исключение, если оно обрабатывается таким образом?
В ней и функциональности больше, да и API на мой взгляд чище.
Кроме того есть ее порт для промисов — www.npmjs.com/package/async-q
Также полезна, так как организация высокоуровневых шаблонов асинхронности(ограничение количества потоков, очереди) в промисах все-таки отсутсвует
обязывает разработчика все конструкции await заключать в блоки trycatchА нельзя сделать один trycatch где-то наверху, там где реквест/задача начинается?
Проверил на базовых примерах — внешний try-catch прекрасно работает
Может сломаться, если не await-ить внутренние вызовы, но тут и try-catch-hell не спасет
Как следствие может сломаться на изощренных юзкейсах, вроде «послать все запросы в цикле, их промисы запомним в массив, а потом в однопоточном цикле их await-ить и обрабатывать»
Но в таких нетривиальных случаях пожалуй лучше будет использовать библиотеки управления потоком, или как workaround, вызвать перед циклом
await Promise.race(results)
Как следствие try-catch-и можно размещать согласно логике приложение(там где вы хотите реально обработать ошибку), например на уровне обработчика запроса — точно также, как это делается для синхронного кода
(async function main() {
await func1();
await func2();
})().catch((err) => console.error(err))
Также, вероятно на верхнем уровне асинхронные функции вызываются синхронно, одна за другой, потому что должны идти параллельно и независимо (иначе они уже были бы обернуты в асинхронную функцию, либо цепочку промисов).
Тогда стоит рассматривать эти функции как верхнеуровневые, и достаточно добавить try-catch только в них, либо вообще обработать их ошибки на уровне модуля
func1();
func2();
async function func1() {
try {
// ...
} catch(e) {
console.log(e);
}
}
async function func2() {
try {
// ...
} catch(e) {
console.log(e);
}
}
func1().catch(e => console.log(e));
func2().catch(e => console.log(e));;
async function func1() {
// ...
}
async function func2() {
// ...
}
В итоге try-catch все еще не нужен в каждой асинхронной функции, и мы получаем обработку ошибок на верхнем уровне(и в тех функциях где она нужна по логике приложения)
При этом, если исключения доходят до верхнего уровня, то логично использовать uncaughtException/rejectionHandled
Обратите внимание на вот такой подход: https://m.habr.com/post/339606/. Все try/catch уйдут, но обработка ошибок останется
const { error, data } = await wrapper(fetchData(2000, false));
if (!error) {
Что-то мне это напоминает… :)

function Defer(){
var status;
this.resolve = function(value){
status = {
type: 'resolved',
value: value
};
};
this.reject = function(value){
status = {
type: 'rejected',
value: value
};
};
var that = this;
this.promise = function(){
return promise = new Promise((resolve, reject) => {
if(status){
if(status.type === 'resolved'){
resolve(status.value);
} else {
reject(status.value);
}
} else {
that.resolve = function(value){
resolve(value);
}
that.reject = function(value){
reject(value);
}
}
});
};
}
С ним можно делать более читаемый код:
const fetchData = (duration, rejectPromise) => {
var defer = new Defer();
setTimeout(() => {
if(rejectPromise){
defer.reject({
error: 'Error Encountered',
status: 'error'
});
} else {
defer.resolve({
version: 1,
hello: 'world',
});
}
}, duration);
return defer.promise();
}
Лично мне наоборот сложнее, когда функций больше. У них ещё разные контексты исполнения, разные this (arrow functions не везде), где-то его нужно передавать, где-то нет… опять callback hell, в общем.
Откуда вы взяли цифру 20? new Promise
добавляет только одну дополнительную функцию в код, зато вы получаете важное преимущество — синхронно выброшенное исключение перехватится и зарежектит промис. Вы гарантированно никогда не получите синхронного исключения.
Тем, что не нужно создавать 20 вложенных друг в друга функций.Это наверное при каком-то неправильном использовании, есть пример? Промисы можно более менее линейно «стыковать».
На самом деле, для подхода, описанного в статье, необязательно даже wrapper писать. Достаточно резолвить промис с ошибкой вместо режекта.
const fetchData = async (rejectPromise) =>
new Promise(resolve => {
if (rejectPromise) {
resolve({ error: "Error Encountered" });
} else {
resolve({ data: { version: 1, hello: "world" } });
}
});
// использование будет таким же
const { error, data } = await fetchData(true);
Разумеется, это будет работать только для вашего кода, для библиотек нужна обертка.
Согласен, некоторые разработчики отрицательный результат вызова функции возвращают через исключение (особенно часто это встречается в валидаторах), что бывает очень неудобно. Но лучше сделать обертки для этих функций до их использования, добавив обработку нужных исключений, нежели оборачивать каждый вызов.
JavaScript. Работаем с исключениями и данными в конструкциях async/await без блоков try-catch