Q Promises — преобразование отклонений в необработанные исключения

При отладке с помощью Chrome у отладчика есть некоторые тонкости для навигации по стеку вызовов необработанных исключений. Я начал использовать обещания Q, и теперь необработанные исключения по существу преобразуются в отклоненные обещания. Это нормально, за исключением случаев, когда дело доходит до отладки. Рассмотрим пример:

promise.done(do_work)

Любые исключения, сгенерированные в функции do_work, будут перехвачены Q, а затем сгенерированы как необработанные исключения в следующем такте. Проблема в том, что стек вызовов в отладчике Chrome скучен и просто представляет собой сброс Q. Это правда, что свойство стека исключения правильное, но это всего лишь всплывающая подсказка в отладчике Chrome, и ее использование раздражает.

Я пробовал такие вещи, как:

promise.done(do_work, function(e){throw e;})

Но эти исключения снова перехватываются Q; и, впрочем, это неважно, потому что к моменту, когда вы доберетесь до обработчика ошибок, стек будет уже со следующего тика. Я также пробовал играть с Q.onerror и другими, но у них та же проблема.

Есть ли способ заставить Q действительно игнорировать исключения для некоторых вызовов во время разработки, чтобы отладчик Chrome мог получить хороший исходный стек вызовов для работы, прежде чем Q перейдет к следующему такту процесса?


person drarmstr    schedule 23.12.2013    source источник
comment
Именно это мне не нравится в Q — путаница между ошибками и отказами. Поклонники Q, конечно, возразят и скажут вам, что это лучшая функция со времен нарезанного хлеба. Насколько мне известно, вы должны согласиться со склонностями Q, установив обработчик(и) .fail() (псевдоним .catch()), который выполняет операторы console.log() (или аналогичные) для отображения сообщений об ошибках по вашему выбору.   -  person Beetroot-Beetroot    schedule 24.12.2013
comment
.fail()/.catch() также не решают эту проблему, потому что к тому времени, когда они выполняются, вы находитесь на следующем такте процесса и теряете исходный контекст. Однако я нашел обходной путь; который я опубликую в качестве ответа.   -  person drarmstr    schedule 24.12.2013
comment
Я не понимаю вашей точки зрения насчет следующего тика процесса. Конечно, выданная ошибка передается в качестве параметра обработчику .error(), где она может быть обработана и/или повторно выдана.   -  person Beetroot-Beetroot    schedule 24.12.2013
comment
Да, это так. Однако мы больше не находимся в контексте выполнения этой ошибки. Итак, хотя мы можем посмотреть на свойство стека ошибки, чтобы увидеть, каким был стек, когда произошла ошибка, мы не можем интерактивно перемещаться по этому стеку вызовов в отладчике или проверять состояние локальных переменных, замыканий и т. д. в контексте того, что они делают. были, когда ошибка действительно произошла. Нет ничего плохого в том, как Q обрабатывает ошибку на основе спецификации Promises/A. Однако такая обработка усложняет отладку ошибок. Надеюсь, мой обходной ответ ниже также поможет прояснить разницу.   -  person drarmstr    schedule 24.12.2013
comment
Drarmstr, ага, спасибо, теперь я лучше понимаю, но, к сожалению, не могу дать дельный совет :(   -  person Beetroot-Beetroot    schedule 25.12.2013


Ответы (2)


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

Следующие действия позволяют перехватывать необработанные исключения в функции do_work в отладчике Chrome. Вы не только получаете удобную навигацию по стеку вызовов, но и можете проверять значения и состояние в момент сбоя.

Вместо:

promise.done(do_work)

Использовать:

promise.done -> setTimeout do_work, 0

Или в JavaScript:

promise.done(function(){setTimeout(do_work,0);});

С помощью этого хака вызов do_work() по-прежнему зависит от выполнения промиса, но затем отделяется от механизма Q для перехвата исключений, поэтому ошибки можно легче отлаживать с помощью Chrome. Я также могу использовать этот шаблон для отладки функций в середине цепочки обещаний:

Вместо:

new_promise = promise.then(do_intermediate_work)

Использовать:

new_promise = promise.then -> Q.promise (resolve)-> setTimeout(
    ->
        do_intermediate_work()
        resolve()
    0
)

Или в JavaScript:

new_promise = promise.then(function() {
    return Q.promise(function(resolve) {
        setTimeout((function() {
            do_intermediate_work();
            resolve();
        }), 0);
    });
});

Я надеюсь, что здесь мне не хватает лучшего решения, так как это похоже на проблему удобства использования с текущей цепочкой инструментов и отладкой с обещаниями.

person drarmstr    schedule 24.12.2013

В этом смысле нет полезного оригинального стека вызовов, потому что стеки вызовов не работают через асинхронные границы. Я не уверен, в чем проблема с «фальшивым» расширенным стеком, который вы получаете с включенной длинной трассировкой стека (за исключением того факта, что он проходит только 1 уровень, а не полностью) - они так же читаемы, как и обычные трассировки стека. ...

person Esailija    schedule 24.12.2013
comment
Помимо тонкости отображения стека вызовов, вам также необходимо выполнять выполнение в контексте ошибки, чтобы отладчик Chrome мог перемещаться вверх и вниз по стеку для просмотра состояния локальных переменных, этого контекста, замыканий и т. д. Очень полезно иметь возможность проверять это состояние при возникновении ошибки. Если вы получаете уведомление об ошибке только позже, вне этого контекста, вы не можете проверить это состояние. - person drarmstr; 24.12.2013
comment
@drarmstr да, меня это не особо волнует, просто обычно это замедляло меня или не было необходимо .. удачи :) - person Esailija; 25.12.2013