Как отлаживать код на основе обещаний в узле?

Я использую отличную библиотеку Cujo When, чтобы обеспечить реализацию Promises/A+ для моего узла. проект, хотя этот вопрос не зависит от узла.

Как правило, When великолепен: он позволяет мне писать более удобный и читаемый код.

Однако, когда мои обратные вызовы неожиданно терпят неудачу (доступ к свойству нулевой переменной и т. д.), исключения эффективно проглатываются, когда, как кажется, указан спецификацией Promises/A+. К сожалению, это означает, что я не получаю никаких отзывов об ошибке (кроме того, что обратный вызов перестает выполняться в этот момент). Нет типа ошибки или сообщения, нет номера строки.

Проиллюстрировать:

// hypothetical asynchronous database query
database.query(queryDetails).then(function(result) {

  var silly = 3.141592654;
  silly(); // TypeError: number is not a function!

  process(result); // this code is silently never executed

});

Я могу придумать несколько (неприемлемых) способов решить эту проблему:

  • предоставление обратных вызовов при сбое для каждого вызова then (для вывода причины/исключения на консоль)
  • обертывание всех тел обратного вызова в try-catches
  • засорение кодовой базы "логами ориентиров" аля console.log('I got here 123')

Я просто делаю это неправильно? Конечно, я не одинок в том, что не могу отлаживать код, основанный на промисах. Есть ли очевидное решение, которое мне не хватает?


person aaaidan    schedule 08.01.2014    source источник
comment
Предпочитайте Bluebird, чем когда. Он имеет ИЗУМИТЕЛЬНЫЕ трассировки стека (намного лучше). О, и не беспокойтесь о производительности, она также намного быстрее. Кроме того, bluebird регистрирует необработанные исключения в stderr (обычно на консоль) с полной трассировкой стека. Он также имеет очень хорошую обработку ошибок с помощью catch и тому подобного.   -  person Benjamin Gruenbaum    schedule 08.01.2014
comment
Спасибо Бенджамин - это ответ?   -  person aaaidan    schedule 08.01.2014
comment
Нет, это предложение библиотеки после того, как я сам разобрался с проблемой. Предложения библиотеки - плохие ответы :) Я не уверен, что у When есть хороший unhandledExceptionHandler. Честно говоря, по сравнению с Bluebird все остальные библиотеки для промисов в Node кажутся очень уж наивными.   -  person Benjamin Gruenbaum    schedule 08.01.2014
comment
необработанный обработчик исключений!! Просто прочитав об этом сейчас, у него есть монитор. Странно, что эта проблема не решается спецификацией Promises/A+...   -  person aaaidan    schedule 08.01.2014
comment
Спецификация Promises/A+ касается не практической реализации промиса (в ней даже не указан способ создания промиса), а минимальных требований для хорошего взаимодействия.   -  person Esailija    schedule 08.01.2014
comment
Я знаю, что это может быть скучно, но обратные вызовы onError похожи на блоки catch в синхронном программировании, а предоставление блоков try без перехвата — известная плохая практика.   -  person pedroassis    schedule 04.02.2014
comment
@pedroassis да, мне просто не нравится тяжелая вложенность, которая идет с традиционными обратными вызовами (в стиле узла). Я фанат промисов и всегда использую их хотя бы один раз в цепочке.   -  person aaaidan    schedule 10.02.2014
comment
@aaaidan, что я имею в виду, вы должны предоставить обратные вызовы onError для своих промисов, если они не являются исключениями времени выполнения, если они есть, вам понадобится реализация обещания с хорошей отладкой   -  person pedroassis    schedule 11.02.2014
comment
@pedroassis Да, в том-то и дело, что суть этого вопроса в тех неприятных ошибках времени выполнения, которые особенно распространены в первом фрагменте кода.   -  person aaaidan    schedule 15.02.2014
comment
Суммированные комментарии с дополнительными данными и when конкретным решением ответа.   -  person Benjamin Gruenbaum    schedule 13.04.2014


Ответы (2)


Обновление, сентябрь 2016 г. NodeJS 6.6.0+ и 7.0+ будет автоматически предупреждать о необработанных отклонениях. Запустите node с --trace-warnings, чтобы получить разумные трассировки стека. Все еще не так хорошо, как то, что дает вам Bluebird, но намного лучше, чем раньше.


Итак, резюмируем информацию из комментариев и добавляем кое-что.

  • В спецификации Promises/A+ нет ничего, что диктовало бы, как решать эту проблему. Спецификация касается минимальных требований для хорошего взаимодействия между разными библиотеками промисов, поэтому одна библиотека промисов может использовать промисы, созданные в другой, и наоборот.
  • Несколько библиотек решают эту проблему, включая метод .done, который явно объявляет, что цепочка завершена, что приводит к выдаче неперехваченных отклонений. Библиотеки наподобие When и Q решают проблему таким образом. Например, если ваш .then после .query был .done, вы получите длинную трассировку стека.
  • Более новые, менее наивные реализации обещаний, такие как Bluebird, решают эту проблему, автоматически определяя, возможно, неперехваченные отказы и громко регистрируя их для вас. Они также дают вам крючок. Когда есть экспериментальная поддержка для этого с помощью монитора.

В качестве таких:

 require('when/monitor/console'); // when will now log async rejections used with
                                  // `then` , this is experimental in when.

С синей птицей

Promise.longStackTraces(); // Bluebird always logs async rejections but with this 
                           // option it will stitch the asynchronous context stack
                           // for you in your methods.
  • Поведение обещаний ES6 в этом не указано. В отношении этого нет явных требований к нативным реализациям. Однако я знаю, что поставщики работают над этим, и я ожидаю, что движки начнут понимать это даже в нативных реализациях.
person Benjamin Gruenbaum    schedule 13.04.2014
comment
Какого черта это не включено по умолчанию?! Кроме того, даже если я включу это, я не получу номер строки или трассировку стека ошибок. Как я должен найти, где это произошло? - person Tomáš Zato - Reinstate Monica; 28.05.2017
comment
@Tomas, он включен по умолчанию, и вы можете войти в файл, все эти функции занимают сотни часов, и нам действительно не помешала бы помощь в улучшении инструментов. Извиняюсь за плохой дефолт, когда я начал над ним работать события не существовало и исключения просто проглатывались, после этого мы добавили событие - а потом в 6.6 трассировку по умолчанию. V8 добавляет util.promisify, поддержку async_hooks и улучшенные трассировки — мы работаем над улучшением, но для большинства из нас это просто хобби. Тем не менее, извините за дерьмовый опыт отладки! - person Benjamin Gruenbaum; 29.05.2017
comment
Спасибо за ответ. Но на самом деле все, что мне нужно было сделать, это поймать исключение и распечатать трассировку стека. Код, который предупреждает о необработанном исключении, явно перехватывает его, поэтому он может точно так же распечатать трассировку стека, пока вы ее перехватываете? В любом случае спасибо за то, что посвятили себя этому. Я, конечно, благодарен за Node.js и все, что в нем есть, это моя самая любимая платформа для многих вещей, которые я делаю! - person Tomáš Zato - Reinstate Monica; 29.05.2017

Вот как я определяю, когда обещание было отклонено на узле, но не перехвачено:

if (typeof process === 'object') {
    process.on('unhandledRejection', (error, promise) => {
        console.error("== Node detected an unhandled rejection! ==");
        console.error(error.stack);
    });
}

В дополнение к этому вы можете использовать эту обертку-обезьяну для предоставления длинных трассировок стека для обещаний Node ES6. Он производит аналогичный вывод Q longStackSupport. Я бы не рекомендовал его для использования вне кода разработки из-за проблем с производительностью. (У меня это работает в Node v4.4.1. Я еще не тестировал его в Chrome или Firefox.)

person joeytwiddle    schedule 19.04.2016
comment
Этот ответ помог мне обнаружить неопределенные функции в обещаниях. - person Sydney; 04.04.2017