Обработка ошибок

Заметка

Поведение, описанное в этой статье, доступно только тогда, когда включена предварительная версия функции Управление ошибками на уровне формул в разделе Параметры>Предстоящие функции>Предварительная версия. Дополнительные сведения: Управление включением функций

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

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

Как создатель приложения, вы можете взять под контроль ошибки в своем приложении:

  • Обнаружение и обработка ошибки. Если есть вероятность возникновения ошибки, можно написать формулы приложения, чтобы определить состояние ошибки и повторить операцию. Конечному пользователю не нужно беспокоиться о том, что произошла ошибка, потому что создатель учел эту возможность. Это делается с помощью функций IfError, IsError и IsErrorOrBlank внутри формулы.
  • Сообщение об ошибке. Если ошибка не обрабатывается в формуле, в которой она была обнаружена, ошибка передается обработчику App.OnError. Здесь ошибку уже нельзя заменить, так как она уже произошла и является частью расчетов по формуле. Но вы можете использовать обработчик App.OnError для управления тем, как сообщение об ошибке передается конечному пользователю, включая полное подавление сообщения об ошибке. App.OnError также обеспечивает общую точку отсчета для сообщений об ошибках во всем приложении.
  • Создание и повторное создание ошибки. Наконец, вы можете обнаружить состояние ошибки с помощью собственной логики, условия, специфичного для вашего приложения. Используйте функцию Error для создания пользовательских ошибок. Функция Error также используется для повторного создания ошибки после ее обработки в IfError или App.OnError.

Начало работы

Давайте начнем с простого примера.

  1. Создайте новый экран в приложении на основе холста Power Apps.
  2. Вставьте элемент управления TextInput. По умолчанию ему будет присвоено имя TextInput1.
  3. Вставьте элемент управления Label.
  4. Задайте для свойства Text элемента управления Label формулу
1/Value( TextInput1.Text )

Отображается баннер ошибки с сообщением «значение невозможно преобразовать в число» для элемента управления вводом текста, содержащего «Ввод текста»

У нас есть ошибка, потому что текст по умолчанию элемента управления TextInput равен "Text input" и не может быть преобразован в число. По умолчанию это хорошо: конечный пользователь получит уведомление о том, что в приложении что-то работает не так, как ожидалось.

Очевидно, мы не хотим, чтобы ошибка приветствовала пользователя каждый раз, когда он запускает это приложение. Наверное, все равно "Text input" не является правильным значением по умолчанию для поля ввода текста. Чтобы это исправить, давайте изменим свойство Default элемента управления TextInput на следующее:

Blank()

Отображается баннер ошибки с

Хм, теперь у нас другая ошибка. Математические операции со значением пусто, такие как деление, преобразуют пустое значение в нуль. И это теперь вызывает ошибку деления на ноль. Чтобы исправить это, нам нужно решить, какое поведение подходит для этой ситуации в этом приложении. Ответ может заключаться в том, чтобы отображать пустое значение, когда введенный текст пуст. Этого можно добиться, обернув нашу формулу функцией IfError:

IfError( 1/Value( TextInput1.Text ), Blank() )

Баннер ошибки не отображается, ошибка из-за пустого значения заменена пустым полем

Теперь ошибка заменена допустимым значением, а баннер ошибки исчез. Но, возможно, мы перестарались, функция IfError, использованная нами, покрывает все ошибки, включая ввод неправильного значения, например "hello". Мы можем решить эту проблему, настроив нашу функцию IfError так, чтобы она обрабатывала только деление на ноль и повторно вызывала все остальные ошибки:

IfError( 1/Value( TextInput1.Text ), 
         If( FirstError.Kind = ErrorKind.Div0, Blank(), Error( FirstError ) ) )

Баннер ошибки не отображается, ошибка, вызванная именно делением на ноль, заменена пустым полем, в противном случае ошибка выдается повторно

Итак, давайте запустим наше приложение и попробуем разные значения.

Без какого-либо значения, как при запуске приложения, ответ не отображается, поскольку значение по умолчанию пусто, но также не отображается ошибка, поскольку IfError заменяет ошибку деления на ноль.

Ответ и баннер ошибки не отображаются

Если мы введем 4, мы получим ожидаемый результат 0,25:

Отображается 0.25, и нет баннера с ошибкой

И если мы введем что-то недопустимое, например hello, то получим баннер с ошибкой:

значение не отображается, и отображается баннер ошибки из-за невозможности преобразовать «hello» в число

Это простой вводный пример. Обработку ошибок можно выполнять разными способами, в зависимости от потребностей приложения:

  1. Вместо баннера ошибки мы могли бы показать "#Error" в элементе управления Label с формулой. Чтобы типы замен были совместимы с первым аргументом IfError, нам нужно явно преобразовать числовой результат в текстовую строку с помощью функции Text.
    IfError( Text( 1/Value( TextInput1.Text ) ), 
             If( FirstError.Kind = ErrorKind.Div0, Blank(), "#Error" )
    
    нет баннера ошибки, вместо этого в качестве результата отображается #Error
  2. Вместо того, чтобы оборачивать этот конкретный экземпляр в IfError, мы могли бы написать централизованный обработчик App.OnError. Мы не можем заменить отображаемую строку на "#Error", так как ошибка уже произошла и App.OnError предоставляется только для управления отчетами.
    If( FirstError.Kind <> ErrorKind.Div0, Error( FirstError ) )
    

Распространение ошибок

Ошибки проходят через формулы так же, как и в Excel. Например, в Excel, если ячейка A1 имеет формулу =1/0, то в A1 отобразится значение ошибки #DIV0!:

Таблица Excel с A1=1/0 и отображением #DIV/0! в ячейке

Если ячейка A2 ссылается на A1 с такой формулой, как =A1*2, то ошибка распространяется и на эту формулу:

Таблица Excel с A2=A1*2 и отображением #DIV/0! в ячейке

Ошибка заменяет значение, которое в противном случае было бы вычислено. Результата умножения в ячейке A2 нет, только ошибка деления в A1.

Power Fx работает так же. В общем случае, если ошибка передается в качестве аргумента функции или оператора, операция не выполняется, а ошибка ввода передается как результат операции. Например, Mid( Text( 1/0 ), 1, 1 ) вернет ошибку деления на ноль, так как самая внутренняя ошибка проходит через функцию Text и функцию Mid:

Баннер ошибки, показывающий недопустимую операцию: деление на ноль

Как правило, ошибки не проходят через свойства элемента управления Power Apps. Давайте расширим предыдущий пример дополнительным элементом управления, который отображает, является ли свойство Text первой метки состоянием ошибки:

Ошибка не отображается на втором элементе управления подписи

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

Большинство функций и операторов следуют правилу «ошибка на входе, ошибка на выходе», но есть и некоторые исключения. Функции IsError, IsErrorOrBlank и IfError предназначены для работы с ошибками, поэтому они могут не возвращать ошибку, даже если она передается им.

Наблюдение за ошибками

Ошибки не наблюдаются, пока их значение не используется.

В результате функции If и Select также могут не возвращать ошибку, если она передана им. Рассмотрим формулу If( false, 1/0, 3 ). В этой формуле присутствует ошибка деления на ноль, но поскольку If не принимает эту ветвь из-за false, Power Fx и Power Apps не будет сообщать об ошибке:

Баннер ошибки не отображается с функцией «Если» в свойстве «Текст» подписи

Использование функции Set с ошибкой не сообщит об ошибке в момент помещения ошибки в переменную. Например, в Power Apps вот формула в App.OnStart, которая помещает ошибку деления на ноль в переменную x:

Баннер ошибки не отображается при вызове функции Set в App.OnStart

Об ошибке не сообщается, поскольку на x не ссылаются. Однако в тот момент, когда мы добавляем элемент управления Label и устанавливаем для его свойства Text значение x, отображается ошибка:

Баннер ошибки отображается, когда элемент управления подписи ссылается на переменную x

Ошибки в формулах можно обнаруживать с помощью функций IfError, IsError и IsErrorOrBlank. С помощью этих функций вы можете вернуть альтернативное значение, выполнить альтернативное действие или изменить ошибку до того, как она будет замечена и о ней будет сообщено.

Сообщение об ошибках

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

В отличие от Excel, не всегда есть удобное место для отображения результата ошибки, поскольку результат формулы может управлять таким свойством, как координаты X и Y элемента управления, для которого нет удобного места для отображения текста. Каждый узел Power Fx контролирует то, как ошибки в конечном итоге отображаются для конечного пользователя, и насколько создатель может контролировать этот процесс. В Power Apps отображается баннер ошибки, а App.OnError используется для управления способом сообщения об ошибке.

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

Переменные области FirstError и AllErrors предоставляют контекстную информацию об ошибке или ошибках. Это предоставляет информацию о типе ошибки и о том, где она возникла и где она была обнаружена.

Остановка после ошибки

Формулы поведения поддерживают выполнение действий, изменение баз данных и изменение состояния. Эти формулы позволяют выполнять более одного действия в последовательности с использованием оператора связывания ; (или ;;, в зависимости от языкового стандарта).

В этом случае, например, элемент управления grid показывает, что находится в таблице T. Каждый раз при выборе кнопки изменяется состояние в этой таблице двумя вызовами Patch:

Анимация, показывающая, как две записи в таблице T обновляются с использованием случайных чисел после каждого нажатия кнопки

В формуле связанного поведения действия не прекращаются после первой ошибки. Давайте изменим наш пример, чтобы передать недопустимый номер индекса в первом вызове Patch. Второй Patch продолжает работу, несмотря на эту предыдущую ошибку. Первая ошибка сообщается конечному пользователю и отображается как ошибка в Studio на элементе управления:

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

IfError можно использовать для остановки выполнения после ошибки. Подобно функции If, третий аргумент этой функции предоставляет место для размещения действий, которые должны выполняться только в случае отсутствия ошибки:

Анимация, показывающая отсутствие изменений в обеих записях в таблице T, поскольку IfError препятствует выполнению второй операции после ошибки

Если во время одной из итераций ForAll возникает ошибка, остальные итерации не останавливаются. ForAll предназначен для независимого выполнения каждой итерации, что позволяет осуществлять параллельное выполнение. По завершении ForAll будет возвращена ошибка, содержащая все обнаруженные ошибки (путем проверки AllErrors в IfError или App.OnError).

Например, следующая формула приведет к тому, что ForAll вернет две ошибки (для деления на ноль для Value, равного 0, дважды) и Collection будет иметь три записи (для случаев, когда Value отлично от 0): [1, 2, 3].

Clear( Collection ); 
ForAll( [1,0,2,0,3], If( 1/Value > 0, Collect( Collection, Value ) ) );

Работа с несколькими ошибками

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

По умолчанию о первой ошибке сообщается конечному пользователю. В этом примере оба вызова Patch завершатся ошибкой, второй с ошибкой деления на ноль. Пользователю показывается только первая ошибка (об индексе):

Первая ошибка индекса отображается в баннере ошибки, о второй ошибке не сообщается

Функция IfError и App.OnError могут получить доступ ко всем обнаруженным ошибкам с помощью переменной области видимости AllErrors. В этом случае мы можем установить это значение для глобальной переменной и посмотреть на обе возникшие ошибки. Они отображаются в таблице в том порядке, в котором они возникли:

Сбор ошибок в глобальную переменную PatchErrors, где мы можем видеть, что присутствуют обе ошибки

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

Ошибки в таблицах

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

Например, рассмотрим этот элемент управления таблицей данных в Power Apps:

Таблица данных, показывающая ошибку для поля Reciprocal при вводе 0, приводящем к ошибке деления на ноль

Вычисление в AddColumns обнаружило ошибку деления на ноль для одного из значений. Для этой одной записи в столбце Reciprocal есть значение ошибки (деление на ноль), но в других записях ее нет, и все в порядке. IsError( Index( output, 2 ) ) возвращает false и IsError( Index( output, 2 ).Value ) возвращает true.

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

Возьмем этот пример. Здесь в исходной таблице нет ошибок, но акт фильтрации создает ошибку всякий раз, когда Value равно 0:

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

Значения –5 и –3 правильно отфильтрованы. Значения 0 приводят к ошибке при обработке фильтра, поэтому неясно, должна ли запись быть включена в результат или нет. Чтобы обеспечить максимальную прозрачность для конечных пользователей и помощь создателям в отладке, мы включаем запись об ошибке вместо оригинала. В этом случае IsError( Index( output, 2 ) ) возвращает значение true.

Ошибки источника данных

Функции, которые изменяют данные в источниках данных, такие как Patch, Collect, Remove, RemoveIf, Update, UpdateIf и SubmitForm, сообщают об ошибках двумя способами:

  • Каждая из этих функций вернет значение ошибки как результат операции. Ошибки можно обнаружить с помощью функции IsError и заменить или подавить с помощью функций IfError и App.OnError, как обычно.
  • После операции функция Errors также вернет ошибки для предыдущих операций. Это может быть полезно для отображения сообщения об ошибках на экране формы без необходимости захвата ошибки в переменной состояния.

Например, эта формула проверит наличие ошибки из Collect и отобразит пользовательское сообщение об ошибке:

IfError( Collect( Names, { Name: "duplicate" } ),
         Notify( $"OOPS: { FirstError.Message }", NotificationType.Warning ) )

Функция Errors также возвращает информацию о прошлых ошибках во время операций времени выполнения. Это может быть полезно для отображения ошибки на экране формы без необходимости захвата ошибки в переменной состояния.

Повторный вызов ошибок

Иногда некоторые потенциальные ошибки ожидаются и могут быть безопасно проигнорированы. Внутри IfError и App.OnError, если обнаружена ошибка, которая должна быть передана следующему вышестоящему обработчику, ее можно сгенерировать заново с помощью Error( AllErrors ).

Создание собственных ошибок

Вы также можете создавать свои собственные ошибки с помощью функции Error.

Если вы создаете свои собственные ошибки, рекомендуется использовать значения выше 1000, чтобы избежать возможных конфликтов с будущими значениями системных ошибок.

Значения перечисления ErrorKind

Перечисление ErrorKind Стоимость Описание:
AnalysisError 18 Системная ошибка. Возникла проблема с анализом компилятора.
BadLanguageCode 14 Был использован недопустимый или нераспознанный код языка.
BadRegex 15 Недопустимое регулярное выражение. Проверьте синтаксис, используемый с функциями IsMatch, Match или MatchAll.
Конфликт 6 Обновляемая запись уже была изменена в источнике, и конфликт необходимо разрешить. Распространенным решением является сохранение любых локальных изменений, обновление записи и повторное применение изменений.
ConstraintViolated 8 Запись не прошла проверку ограничений на сервере.
CreatePermission 3 У пользователя нет разрешения на создание записей для этого источника данных. Например, была вызвана функция Collect.
DeletePermissions 5 У пользователя нет разрешения на удаление записей для этого источника данных. Например, была вызвана функция Remove.
Div0 13 Деление на ноль.
EditPermissions 4 У пользователя нет разрешения на создание записей для этого источника данных. Например, была вызвана функция Patch.
GeneratedValue 9 На сервер было ошибочно передано значение для поля, которое автоматически вычисляется сервером.
InvalidFunctionUsage 16 Недопустимое использование функции. Часто один или несколько аргументов функции неверны или используются недопустимым образом.
FileNotFound 17 Не удалось найти хранилище SaveData.
InsufficientMemory 21 На устройстве недостаточно памяти или места в хранилище для выполнения операции.
InvalidArgument 25 В функцию был передан недопустимый аргумент.
Внутренняя 26 Системная ошибка. Возникла внутренняя проблема с одной из функций.
MissingRequired 2 Отсутствовало обязательное поле записи.
Сеть 23 Возникла проблема со связью по сети.
None 0 Системная ошибка. Нет ошибок.
Неприменимо 27 Нет доступных значений. Полезно, чтобы отличить пустое значение, которое может рассматриваться как ноль в числовых вычислениях, от пустых значений, которые должны быть помечены как потенциальная проблема, если значение используется.
Не найдено 7 Не удается найти запись. Например, запись, которую необходимо изменить в функции Patch.
NotSupported 20 Операция не поддерживается этим проигрывателем или устройством.
Числовое 24 Числовая функция использовалась ненадлежащим образом. Например, Sqrt с аргументом –1.
QuoteExceeded 22 Превышена квота хранилища.
ReadOnlyValue 10 Столбец доступен только для чтения и не может быть изменен.
ReadPermission 19 У пользователя нет разрешения на чтение записей для этого источника данных.
Синхронизировать 1 Произошла ошибка в источнике данных. Чтобы узнать больше, просмотрите столбец "Message".
Неизвестно 12 Произошла неизвестная ошибка.
Проверка 11 Запись не прошла проверку.