Як налагодити обіцянки javascript?


75

Я намагаюся зрозуміти, як налагоджувати асинхронний код, який базується на обіцянках. Під Promises я маю на увазі обіцянки на основі ECMAScript 6, а під налагодженням я маю на увазі використання вбудованого налагоджувача chrome або firefox.

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

Я спробував такі:

console.log(new Error('Error occured'));
throw new Error('Throwing an Error');
return new Error('Error returned by the onRejected function');
reject(new Error('Pass Error to the reject function'));

Але жодне з них не повертає фактичну помилку в коді або трасування стека.

Тож моє запитання - як правильно налагоджувати javascript Promises?


На основі html5rocks.com/en/tutorials/es6/promises я б скоріше зробив: reject('Error')але чи можете ви опублікувати jsfiddle, щоб нам було з чим конкретно працювати?
Ренра

3
Це одна з головних переваг роботи з бібліотекою обіцянок Bluebird. Він фіксує сліди стека для вас у будь-який час, коли у вас є не оброблене відхилення або виняток у коді обіцянки. Я використовую його з node.js, і це економить ТОН часу. Я, чесно кажучи, не знаю, як отримати ту саму функціональність за допомогою вбудованих обіцянок.
jfriend00

2
@Renra, будь ласка, не давайте поганих порад - кидати (або відкидати) примітиви - це неприємна звичка, яка набагато ускладнює налагодження.
Бенджамін Груенбаум

Відповіді:


61

Це чудова тема для обговорення, сумна новина полягає в тому, що насправді це досить важко з рідними обіцянками.

Налагодження сирих обіцянок ES6 у Chrome жахливо. Це пов’язано з тим, що вони мовчки придушують помилки, і всякий раз, коли ви пропустите улов, це не дасть вам жодних ознак того, що обіцянка не вдалася. Оновлення: Chrome тепер реєструє необроблені відхилення (див. Це посилання, щоб дізнатися, як це зробити)

 Promise.resolve("foo").then(function(){
      throw new Error("You will never see this");// silent failure
 });

У Firefox справи стають трохи кращими, оскільки вони виконують невправлене виявлення відхилення, однак воно все ще не працює, і якщо ви призначили обіцянку в будь-якому місці, це не спрацює.

Отже, що можна зробити?

Включіть Bluebird - це надмірна кількість обіцянок ES6, і ви можете поміняти його прямо всередині, він має багатший API, він швидший і має дивовижні сліди стека . Він побудований з урахуванням налагодження та включає великі засоби обробки помилок.

Після включення Bluebird зателефонуйте:

Promise.longStackTraces();

Що трохи уповільнить (це все одно буде дуже швидко) і дасть вам дивовижні повідомлення про помилки. Наприклад:

Promise.resolve().then(function outer() {
    return Promise.resolve().then(function inner() {
        return Promise.resolve().then(function evenMoreInner() {
            a.b.c.d()
        });
    });
});

У рідних обіцянках - це буде тихий збій, і його буде дуже важко налагодити - із обіцянками Bluebird це покаже велику червону помилку у вашій консолі за замовчуванням, що дає вам:

ReferenceError: a is not defined
    at evenMoreInner (<anonymous>:6:13)
From previous event:
    at inner (<anonymous>:5:24)
From previous event:
    at outer (<anonymous>:4:20)
From previous event:
    at <anonymous>:3:9
    at Object.InjectedScript._evaluateOn (<anonymous>:581:39)
    at Object.InjectedScript._evaluateAndWrap (<anonymous>:540:52)
    at Object.InjectedScript.evaluate (<anonymous>:459:21)

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


1
Що, ймовірно, уповільнює це той факт, що він повинен позначати кожен номер рядка у вашому коді під час створення стека, що зробить довший код трохи більшим. Хоча для налагодження, це, безумовно, варто уповільнення.
Ніл

2
Ну, йому потрібно з’єднати сліди стека разом, але лише на помилки - як правило, Bluebird дуже швидкий, і продуктивність все ще дуже хороша з довгими слідами стека на стороні клієнта.
Бенджамін Груенбаум

Велике спасибі за вашу відповідь, принаймні зараз я знаю, що я не єдина, хто бореться з цим. Буде точно вивчати Bluebird. Крім того, спробуємо зробити JSFiddle, щоб продемонструвати, що помилки не відображаються, коли обіцянка не вдалася. І крім цього - обіцянка, в якій сталася помилка, може насправді показувати свій статус «вирішеною», я думаю, це станеться, якщо обіцянка не остання в ланцюжку.
YemSalat

1
СИНІЙ! Приголомшливо Ого. Я використовував Q і був дуже розчарований.
RandallB

1
Так, Chrome насправді має великі обіцянки налагоджувальної панелі :) Незабаром у канарці оновиться
Бенджамін Груенбаум,

14

Ця відповідь є доповненням до відповіді Бенджаміна Груенбаума: Якщо ви використовуєте оператор catch у ланцюжку обіцянок, ви отримаєте трасування стека за допомогою error.stack :

        Promise.longStackTraces();

        function outer() {
            return Promise.resolve();
        }

        function inner() {
            return Promise.resolve();
        }

        function evenMoreInner() {
            a.b.c.d()
        }

        Promise.resolve()
            .then(outer)
            .then(inner)
            .then(evenMoreInner())
            .catch(function (err) {
                    console.log(err.message);
                    console.log(err.stack);
                });

Повідомлення про помилку:

ReferenceError: a is not defined
at evenMoreInner (test/test_promise.js:58:17)  <<<< HERE's the error!
at Context.<anonymous> (test/test_promise.js:64:23)

2
Дякую, бракувало stackмайна і цікавилося, куди воно поділося, ха-ха!
NiCk Newman

4
Тільки для того, щоб повторити це твердження ... Мене обдурив той факт, що console.log(err)не включає err.stack. Вам потрібно console.log(err.stack)побачити деталі.
Кріс Денніс

Це не спрацювало для мене, поки я не встановив прапорець "асинхронізація", як зазначено у відповіді козла. stackoverflow.com/a/25827268/247696
Флімм

13

* Це не відповідає безпосередньо на ваше запитання, але все ж може бути корисним.

Нещодавно Chrome devtools отримав нову функцію, яка корисна для налагодження асинхронного коду, наприклад Promises.

http://www.html5rocks.com/en/tutorials/developertools/async-call-stack/

По суті, увімкніть прапорець "асинхронізація" на вкладці джерел, і Chrome реконструює для вас стек викликів, як якщо б це був синхронний код.

Знімок екрана


1
Він все ще не друкує помилки на консолі та не надає жодної інформації про те, де сталася помилка. Під помилками я маю на увазі речі, які ви не "плануєте" ловити, наприклад, помилки типу, які можуть виникнути в рамках обіцянки тощо
YemSalat

помилка типу повинна відображатися в консолі, навіть у обіцянці.
dandavis

4
@dandavis ні це не буде.
Бенджамін Груенбаум

9
Google Chrome тепер підтримує стеки викликів Async за замовчуванням і прибрав прапорець: developers.google.com/web/updates/2017/05/…
Ден Сало,

1

Здається, вони працюють з інструментами налагодження в Chrome. Дивіться цю тему для отримання додаткової інформації.

https://code.google.com/p/v8/issues/detail?id=3093

Я не перевіряв, чи це вже є у версії розробника або бета-версії, але я сподіваюся, що це буде скоро. Тоді він може бути включений у звичайну версію в січні 2015 року чи близько того (лише особиста здогадка, абсолютлей не обіцяє, оскільки я навіть не працюю в Google).


Зараз я не бачу жодних інструментів налагодження, характерних для обіцянок у Chrome. Чи ти?
Flimm

1
Google видалив цю функцію кілька місяців тому. Спробуйте замість цього асинхронний помічник. див. цей коміт Про асинхронну налагодження
Йохан Бергенс

1

Найкращий спосіб налагодження обіцяного - прослухати unhandledRejectionподію вашого process.

Наприклад, ось як ви можете встановити його і скинути трасування стека ...

 process.on('unhandledRejection', (reason, p) => {
   console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
   // Stack Trace
   console.log(reason.stack);
 });

Куди б ви поклали цей код? У мене помилка в обіцянці, і я не можу знайти навіть у якому файлі розглянути ...
Стефан,

0

Ви можете додати оператор console.log () у функцію .then () об'єкта Promise: Ви також можете додати в .catch (), якщо потрібно.

genericDbCaller(dbName, collectionName, dbFunctionName, params) {
        return new Promise((resolve, reject) => {
            DatabaseContext.instance.getDbConn(dbName)
                .then((dbConn) => {
                    dbConn.get(collectionName)[dbFunctionName].apply(null, params)
                        .then(

                            (docs) =>{
                    ----->>>        console.log(docs);  
                            resolve(docs);
                        })
                        .catch((e)=>{
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.