«

Стили асинхронного кодирования, используемые в 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.