錯誤處理
Note
只有在設定>即將推出的功能>預覽 中的公式層級錯誤管理預覽功能開啟時,本文描述的行為才會開放使用。 其他資訊:控制啟用哪些功能
發生錯誤。 網路會中斷,儲存空間充滿,未預期的值流入其中。 重要的是,系統面對潛在問題時,您的邏輯仍然能正確運作。
根據預設,系統會透過應用程式的公式來傳遞錯誤,並回報給應用程式的終端使用者。 如此一來,終端使用者便能知道發生未預期的問題,他們可能會以不同的輸入值來修正問題,或者可將問題回報給應用程式擁有者。
身為應用程式製作者,您可以掌控應用程式中的錯誤:
- 偵測及處理錯誤。 如果有可能發生錯誤,您可以撰寫應用程式公式,以偵測錯誤條件並重試作業。 終端使用者不需要擔心發生錯誤,因為製作者已考慮到這些可能姓。 這是透過公式中的 IfError、IsError 和 IsErrorOrBlank 函式來完成。
- 回報錯誤。 如果在遇到錯誤的公式中未處理錯誤,則錯誤隨後會傳送至 App.OnError 處理常式。 在這裡就無法再取代錯誤,因為該錯誤已發生且為公式計算的一部分。 但是您可以使用 App.OnError 控制將錯誤回報給終端使用者的方式,包括將所有錯誤回報隱藏在一起。 App.OnError 還提供了一個通用的阻塞指向,用於整個應用程式的錯誤報告。
- 建立及重新擲回錯誤。 最後,您可能會發現到有自己邏輯有錯誤條件,也就是應用程式特有的條件。 請使用 Error 函式來建立自訂錯誤。 函式也用來在 IfError 或 App.OnError 中完成質詢後重新擲回 Error 函式。
開始使用
讓我們看一個簡單的範例。
- 在 Power Apps 畫布應用程式中建立新畫面。
- 插入 TextInput 控制項。 系統會預設名稱為 TextInput1。
- 插入 Label 控制項。
- 將 Label 控制項的 Text 屬性設定為公式
1/Value( TextInput1.Text )
發生錯誤,因為 TextInput 控制項的預設文字是 "Text input"
,系統無法將其轉換為數字。 根據預設,這是個好事:終端使用者會收到應用程式中未如期運作的通知。
這容易理解,我們不希望使用者每次啟動此應用程式時都會看到錯誤訊息。 有可能 "Text input"
不是文字輸入方塊的正確預設值。 為了解決問題,請將 TextInput 控制項的預設屬性變更為:
Blank()
嗯,系統現在發生不同的錯誤。 使用 blank 的數學運算 (例如除法),會將空白值強制歸零。 現在這會造成除數為零的錯誤。 若要修正此問題,我們必須在此應用程式中判斷這種情形的適當行為。 答案可能是當文字輸入為 blank 時,系統顯示 blank。 我們可以透過使用 IfError 函式來包裝我們的公式來達到這個目的:
IfError( 1/Value( TextInput1.Text ), Blank() )
現在,錯誤會替換成有效值,而且錯誤標語已消失。 但是,我們可能已超過應有情況,我們所用的 IfError 會涵蓋所有的錯誤,包括輸入錯誤的值 (例如 "hello"
)。 我們可以透過調整 IfError 來只包括除數為零的問題,並重新擲回所有其他錯誤:
IfError( 1/Value( TextInput1.Text ),
If( FirstError.Kind = ErrorKind.Div0, Blank(), Error( FirstError ) ) )
因此,讓我們執行應用程式,並嘗試一些不同的值。
當應用程式開始時,並不會顯示任何值,因為預設值為 blank,但也不會顯示任何錯誤,因為 IfError 取代了除數為零的錯誤。
如果我們輸入 4,我們就會得到預期結果 0.25:
如果我們輸入了不允許的值,例如 hello
,則會收到錯誤標語:
這是個簡單的入門範例。 可以透過許多不同的方式處理錯誤,取決於應用程式的需求:
- 我們可以使用公式在標籤控制項中顯示 "#Error",而不使用錯誤標語。 若要讓取代類型與傳送至 IfError 的第一個引數相容,我們需要使用 Text 函式,將數字結果明確轉換成文字字串。
IfError( Text( 1/Value( TextInput1.Text ) ), If( FirstError.Kind = ErrorKind.Div0, Blank(), "#Error" )
- 我們可以撰寫集中式 App.OnError 處理常式,而不需使用 IfError 包裝此特定執行個體。 我們無法替換顯示「#Error」錯誤的字串,因為錯誤已發生,且 App.OnError 僅適用於控制報表。
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 會繼續進行。 系統會將第一個錯誤回報給最終使用者,並在控制項上顯示為 Studio 中的錯誤:
IfError 可用於在出錯后停止執行。 與 If 函式類似,此函式的第三個引數提供放置只有在無錯誤時才會執行的動作:
如果在 ForAll 的其中一個反覆運算期間發生錯誤,則其餘的反覆運算將不會停止。 ForAll 旨在獨立執行每個反覆運算,從而允許並行執行。 ForAll 完成時,系統會傳回錯誤,其中包含所有發生的錯誤 (透過檢查 IfError 或 App.OnError 中的 AllErrors)。
例如,下列公式將會導致 ForAll 傳回兩個錯誤 (0 的 Value
的除數為零,兩次),而且 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 列舉 | 數值 | 描述 |
---|---|---|
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 | 12 | 系統錯誤。 沒有發生錯誤。 |
不適用 | 27 | 沒有可用的值。 適用於區分在數字計算中可視為零處理的 blank 值,以及若使用該值則應將其標記為潛在問題的空白值。 |
NotFound | 7 | 找不到記錄。 例如,要在 Patch 函式中修改的記錄。 |
NotSupported | 20 | 此播放機或裝置不支援作業。 |
數值 | 24 | 數字函數的使用方式不正確。 例如,使用 -1 來 Sqrt。 |
QuoteExceeded | 22 | 已超過儲存空間配額。 |
ReadOnlyValue | 10 | 欄為唯讀且無法修改。 |
ReadPermission | 19 | 使用者沒有資料來源的讀取記錄權限。 |
同步 | 7 | 資料來源回報錯誤。 如需詳細資訊,請檢查訊息欄位。 |
未知 | 12 | 發生錯誤,但屬未知種類。 |
驗證 | 11 | 此記錄未傳遞驗證檢查。 |