Стили асинхронного кодирования, используемые в Javascript, могут быть неинтуитивными для разработчиков, более знакомых с императивными языками, такими как Java или C #. Это может быть опасно, потому что вещи выглядят достаточно знакомыми, чтобы люди могли делать опасные предположения. Это усугубляется тем фактом, что JavaScript очень быстро эволюционировал от обратных вызовов до обещаний и async / await. Это может усложниться, если вам придется работать с кодом, написанным в сочетании трех разных стилей.
Я видел несколько случаев, когда разработчики писали код, используя промисы, и предполагали, что их catch
вызов функции уловит любые возможные ошибки.
Вот упрощенный пример:
function doSomethingAsync() { throw new Error('synchronous'); // we'll never get here return new Promise(resolve => setImmediate(() => resolve())); }
doSomethingAsync() .then(() => console.log('No error!')) .catch((err) => { // we expect to get any errors here console.error(`Do Something very important! ${err}`); });
Очевидно, что человек, написавший этот код, предполагает, что, поскольку ожидается, что doSomething
вернет обещание, любые возникающие ошибки будут отклонены обещаниями, которые будут обнаружены в вызове функции catch
. На самом деле, у doSomething
может быть множество способов выйти из строя, которые не имеют ничего общего с его асинхронной работой. Поскольку JavaScript интерпретируется, мы можем даже получить ошибку из-за простой опечатки или другой синтаксической ошибки.
Если мы запустим показанный выше код, мы получим необработанную ошибку:
Error: synchronous
Не то, что мы могли ожидать. Поскольку ошибка в нашей функции на самом деле не использует обещания, блок catch
никогда не увидит эту ошибку. Это может стать большой проблемой, если мы ожидаем, что в случае возникновения каких-либо ошибок произойдет что-то важное. Например, для нас может быть важно регистрировать конкретную информацию об ошибке.
Хуже того, это может быть трудная ошибка, если вы ее не ищете.
Раньше я исправлял такой код, заключая весь doSomething
в блок try / catch, например:
function doSomethingAsync() { try { throw new Error("synchronous"); // we'll never get here return new Promise(resolve => setImmediate(() => resolve())); } catch (err) { // now our function will always return a promise return Promise.reject(err); } }
doSomethingAsync() .then(() => console.log("No error!")) .catch((err) => { console.error(`Do Something very important! ${err}`); });
Это дает нам ожидаемое поведение:
Do Something very important! synchronous
Теперь, когда async / await доступен в JavaScript, мы можем справиться с этим без использования этого try / catch:
async function doSomethingAsync() { throw new Error("synchronous"); // we'll never get here return new Promise(resolve => setImmediate(() => resolve())); }
doSomethingAsync() .then(() => console.log("No error!")) .catch((err) => { console.error(`Do Something very important! ${err}`); });
Намного приятнее!
Первоначально опубликовано на blog.pickabar.com.