If( FirstError.Kind <> ErrorKind.Div0, Error( FirstError ) )
错误流经公式的方式与在 Excel 中的流动方式非常相似。 例如,在 Excel 中,如果单元格 A1
的公式为 =1/0
,则 A1 将显示错误值 #DIV0!
:
如果单元格 A2
使用诸如 =A1*2
的公式引用 A1
,则错误也会通过该公式传播:
该错误会替换本该计算出的值。 单元格中 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
中的公式:
不会报告任何错误,因为未引用 x
。 但是,当我们添加一个标签控件并将其 Text 属性设置为 x
时,会显示错误:
您可能会在具有 IfError、IsError 和 IsErrorOrBlank 函数的公式内观察到错误。 对于这些函数,您可以返回替代值、采取替代操作或在观察和报告错误之前修改错误。
观察到错误后,下一步是将错误报告给最终用户。
与 Excel 不同,并不总是有一个方便的地方来显示错误结果,因为公式的结果可能会控制控件的 X 和 Y 坐标等属性,但却没有方便的地方来显示一些文本。 每个 Power Fx 主机控制错误最终如何显示给最终用户,以及制作者对该过程的控制程度。 在 Power Apps 中,将显示错误横幅,App.OnError 用于控制报告错误的方式。
请务必注意,App.OnError 无法像 IfError 那样替换错误。 在执行 App.OnError 时,错误已经发生,已通过其他公式传播结果。 App.OnError 仅控制如何将错误报告给最终用户,并为制作者提供一个挂钩以在需要时记录错误。
范围变量 FirstError 和 AllErrors 提供有关一个或多个错误的上下文信息。 这将提供有关错误类型、错误起源位置和观察到错误的位置的信息。
出错后停止
行为公式支持采取行动、修改数据库和更改状态。 这些公式允许使用 ;
链接运算符(或 ;;
,具体取决于区域设置)按序列完成多项操作。
例如,在本例中,网格控件将显示 T
表中的内容。 每个按钮选择都通过两个 Patch 调用来更改此表中的状态:
在链式行为公式中,操作不会在遇到第一个错误后停止。 我们来修改示例,以在第一个 Patch 调用中传递一个无效的索引号。 尽管出现了之前的错误,但第二个 Patch 仍继续运行。 第一个错误会报告给最终用户,并工作室中的控件上显示为错误:
IfError 可用于在出错后停止执行。 与 If 函数类似,此函数的第三个参数提供了一个放置仅在没有错误时才应执行的操作的位置:
如果在 ForAll 的其中一个迭代期间遇到错误,则其余迭代不会停止。 ForAll 旨在独立执行每个迭代,允许并行执行。 当 ForAll 完成时,将返回一个错误,其中包含遇到的所有错误(通过检查 IfError 或 App.OnError 中的 AllErrors 来执行)。
例如,以下公式将导致 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 范围变量时遇到的所有错误。 在这种情况下,我们可以将其设置为全局变量并查看遇到的这两个错误。 它们按遇到它们的相同顺序出现在表中:
非行为公式也可以返回多个错误。 例如,对要更新的一批记录使用 Patch 函数会返回多个错误,每条失败的记录都有一个错误。
表中的错误
正如我们之前看到的那样,错误可以存储在变量中。 错误也可以包含在数据结构中,例如表中。 这很重要,因此任何一条记录上的错误都不会使整个表无效。
例如,假设 Power Apps 中有以下数据表控件:
对于其中一个值,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 枚举