Как отслеживать, какие асинхронные задачи транспортир ждет?

У меня есть угловое приложение среднего размера, и по некоторым причинам некоторые из моих тестов на транспортирщики выходят из строя, когда вы работаете против моей живой производственной среды.

Я уверен, что тайм-ауты происходят из-за того, что транспортир ждет какую-то асинхронную задачу. Я знаю о зонах, и я пытался сохранить все длительные задачи async из ngZone (согласно FAQ), но по какой-то причине транспортир по-прежнему отключается.

Возможно, я что-то пропустил, но не знаю, как отладить проблему. Есть ли способ узнать, какая часть обработчика кода ждет?

NgZone предоставляет только функции, чтобы определить, есть ли микротовары или макростаксы, но не сообщает мне, какой из них.

EDIT: типичный пример такой ошибки таймаута, как показано транспортиром:

Не удалось: время ожидания ожидания асинхронных угловых задач заканчивается через 11 секунд. Это может быть связано с тем, что текущая страница не является угловым приложением. Дополнительную информацию см. В FAQ: https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular

В ожидании элемента с локатором - Локатор: By (css selector, [data-e2e = 'scroll-to-offer]])

Элемент существует на странице (я проверил это вручную), но транспортир все еще не работает.

Ответ 1

У меня была похожая проблема, Тестируемость была нестабильной, и все, что я знал, это то, что у меня есть pendingMacroTasks. Это действительно немного сложно локализовать эти задачи (вы не будете просматривать всю кодовую базу и искать setTimeout).
Однако мне удалось это сделать в конце концов.

Относительно простое решение

Первое, что вы можете сделать, это открыть свои инструменты разработчика в Chrome, нажать Ctrl+O, ввести zone.js и нажать Enter.
Откроется исходный файл zone.js (не Typescript, а еще что-то).
Внутри zone.js найдите Zone.prototype.runTask и установите точку останова внутри этого метода.
Включите точку останова после загрузки вашего приложения, иначе оно попадет слишком много раз.

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

Как только достигнута точка останова, перейдите к task.callback.[[FunctionLocation]], и вы, скорее всего, получите setTimeout или setInterval.
Чтобы исправить это, запустите его за пределами angular зоны.

Немного сложнее решение

Это включает в себя настройку кода Zone.js, но дает вам более постоянную отладочную информацию.

  1. Перейти к node_modules/zone.js/dist/zone.js.
  2. Ищите функцию конструктора Зоны: function Zone(parent, zoneSpec)
  3. Добавьте следующее: this._tasks = {}
  4. Добавьте переменную счетчика в глобальную область: let counter = 0
  5. Найдите Zone.prototype.scheduleTask и добавьте следующее прямо перед вызовом this._updateTaskCount(task,1):

    task.id = counter++;
    this._tasks[task.id] = task;
    
  6. Найдите Zone.prototype.scheduleTask и добавьте следующее прямо перед вызовом this._updateTaskCount(task,-1):

    delete this._tasks[task.id]
    
  7. Перекомпилируйте приложение и запустите его

Предполагая, что вы выполнили вышеуказанные настройки, вы всегда можете получить доступ к отложенным задачам, например так:

tasks = Object.values(window.getAllAngularTestabilities()[0]._ngZone._inner._tasks)

Чтобы получить все ожидающие макрокоманды, которые влияют на состояние тестируемости:

tasks.filter(t => t.type === 'macroTask' && t._state === 'scheduled')

После этого, как и в предыдущем решении, проверьте .callback.[[FunctionLocation]] и исправьте, выполнив за пределами angular зоны.

Ответ 2

Нет особого способа отслеживать асинхронные задачи транспортира. Что вы можете сделать, так это выяснить, какие асинхронные задачи могут блокировать ваш поток. Если вы тестируете, есть две основные рекомендации для отладки: асинхронные задачи в коде приложения и асинхронные задачи в тестах.

В ваших тестах, если вы используете что-то вроде Jasmine, попробовали ли вы что-то вроде изменения jasmine.DEFAULT_TIMEOUT_INTERVAL;?

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

Ответ 3

Джеб отлично поработал, предложив свой путь (какое-то решение лучше, чем ничего). Фактически, есть способ получить список ожидающих задач без исправления файла zone.js. Я понял эту идею, глядя на angular источники: https://github.com/angular/angular/blob/master/packages/core/src/zone/ng_zone.ts#L134

Вот как это сделать:

  1. Импортировать файл node_modules/zone.js/dist/task-tracking.js после zone.js
  2. Затем в своем коде получите экземпляр NgZone и используйте его как
import * as _ from "lodash";
...
const ngZone = moduleInstance.injector.get(NgZone);
setInterval(() => {
    var taskTrackingZone = (<any>ngZone)._inner.getZoneWith("TaskTrackingZone");
    if (!taskTrackingZone) {
        throw new Error("'TaskTrackingZone' zone not found! Have you loaded 'node_modules/zone.js/dist/task-tracking.js'?");
    }
    var tasks: any[] = taskTrackingZone._properties.TaskTrackingZone.getTasksFor("macroTask");
    tasks = _.clone(tasks);
    if (_.size(tasks) > 0) {
        console.log("ZONE pending tasks=", tasks);
    }
}, 1000);