分享方式:


錯誤處理

注意

只有在設定>即將推出的功能>預覽 中的公式層級錯誤管理預覽功能開啟時,本文描述的行為才會開放使用。 其他資訊:控制啟用哪些功能

發生錯誤。 網路會中斷,儲存空間充滿,未預期的值流入其中。 重要的是,系統面對潛在問題時,您的邏輯仍然能正確運作。

根據預設,系統會透過應用程式的公式來傳遞錯誤,並回報給應用程式的終端使用者。 如此一來,終端使用者便能知道發生未預期的問題,他們可能會以不同的輸入值來修正問題,或者可將問題回報給應用程式擁有者。

身為應用程式製作者,您可以掌控應用程式中的錯誤:

  • 偵測及處理錯誤。 如果有可能發生錯誤,您可以撰寫應用程式公式,以偵測錯誤條件並重試作業。 終端使用者不需要擔心發生錯誤,因為製作者已考慮到這些可能姓。 這是透過公式中的 IfErrorIsErrorIsErrorOrBlank 函式來完成。
  • 回報錯誤。 如果在遇到錯誤的公式中未處理錯誤,則錯誤隨後會傳送至 App.OnError 處理常式。 在這裡就無法再取代錯誤,因為該錯誤已發生且為公式計算的一部分。 但是您可以使用 App.OnError 控制將錯誤回報給終端使用者的方式,包括將所有錯誤回報隱藏在一起。 App.OnError 還提供了一個通用的阻塞指向,用於整個應用程式的錯誤報告。
  • 建立及重新擲回錯誤。 最後,您可能會發現到有自己邏輯有錯誤條件,也就是應用程式特有的條件。 請使用 Error 函式來建立自訂錯誤。 函式也用來在 IfErrorApp.OnError 中完成質詢後重新擲回 Error 函式。

開始使用

讓我們看一個簡單的範例。

  1. 在 Power Apps 畫布應用程式中建立新畫面。
  2. 插入 TextInput 控制項。 系統會預設名稱為 TextInput1
  3. 插入 Label 控制項。
  4. 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:

顯示 0.25,沒有錯誤橫幅

如果我們輸入了不允許的值,例如 hello,則會收到錯誤標語:

未顯示任何值,但顯示無法將

這是個簡單的入門範例。 可以透過許多不同的方式處理錯誤,取決於應用程式的需求:

  1. 我們可以使用公式在標籤控制項中顯示 "#Error",而不使用錯誤標語。 若要讓取代類型與傳送至 IfError 的第一個引數相容,我們需要使用 Text 函式,將數字結果明確轉換成文字字串。
    IfError( Text( 1/Value( TextInput1.Text ) ), 
             If( FirstError.Kind = ErrorKind.Div0, Blank(), "#Error" )
    
    沒有任何錯誤橫幅,而是顯示 #Error 做為結果
  2. 我們可以撰寫集中式 App.OnError 處理常式,而不需使用 IfError 包裝此特定執行個體。 我們無法替換顯示「#Error」錯誤的字串,因為錯誤已發生,且 App.OnError 僅適用於控制報表。
    If( FirstError.Kind <> ErrorKind.Div0, Error( FirstError ) )
    

錯誤傳播

錯誤會透過公式流通,就像在 Excel 中那樣。 例如,在 Excel 中,如果儲存格 A1 有公式 =1/0,則 A1 將會顯示錯誤值 #DIV0!

儲存格中顯示 A1=1/0 和 #DIV/0! 的 Excel 試算表

如果儲存格 A2 參照包含 =A1*2 等公式的 A1,則錯誤也會透過該公式進行傳播:

儲存格中顯示 A2=A1*2 和 #DIV/0! 的 Excel 試算表

該錯誤會取代原本系統會計算的值。 系統在儲存格 A2 中進行的乘法運算不會有結果,只有 A1 中除法運算的錯誤。

Power Fx 的運作方式相同。 一般而言,如果將錯誤作為引數提供給函式或運算子,系統就不會進行這項運算,而且輸入錯誤會因為運算結果傳遞出去。 例如,Mid( Text( 1/0 ), 1, 1 ) 將傳回「除數為零的錯誤」,而內部大多數錯誤會透過 Text 函式和 Mid 函式進行傳遞:

錯誤橫幅顯示作業無效:除以零

一般而言,錯誤不會透過 Power Apps 控制項屬性傳遞。 我們延伸先前範例,額外加入可顯示第一個標籤的 Text 屬性是否為錯誤狀態的控制項:

第二個標籤控制項上未顯示錯誤

因為系統會觀察所有控制項屬性的輸入錯誤,所以錯誤不會透過控制項傳播。 錯誤不會消失。

大部分的函式和運算子都會遵循「錯誤輸入,錯誤輸出」規則,但仍有一些外狀況。 函式 IsErrorIsErrorOrBlankIfError 的設計目的是為了處理錯誤,因此即使錯誤已傳遞給這些函式,這些函式可能也不會傳回錯誤。

觀察錯誤

直到使用錯誤的值時才會發現錯誤。

因此,IfSelect 函式可能也不會在錯誤傳入時傳回錯誤。 考慮公式為 If( false, 1/0, 3 )。 此公式中有一個除數為零的錯誤,但是由於 If 因為 false 並未進行該分支,Power Fx 和 Power Apps 不會回報錯誤:

標籤文字屬性中使用 If 函數時,未顯示錯誤橫幅

使用包含錯誤的 Set 函式,將錯誤放入變數時,系統將不會回報錯誤。 例如,在 Power Apps 中,App.OnStart 中有一個公式,會將除數為零錯誤放入 x 變數中:

App.OnStart 中使用 Set 函數呼叫時,未顯示錯誤橫幅

因為未參照 x,系統不會回報錯誤。 不過,當我們新增標籤控制項並將其 Text 屬性設為 x 時,系統就會顯示錯誤:

標籤控制項參考變數 x 時,顯示錯誤橫幅

您可以透過 IfErrorIsErrorIsErrorOrBlank 函式來觀察公式中的錯誤。 使用這些函數,您可以傳回替代值、採取替代動作,或是在發現及回報錯誤前修改錯誤。

報告錯誤

觀察到錯誤之後,下一個步驟是將錯誤回報給終端使用者。

與 Excel 不同,不一定有便利的位置來顯示錯誤結果,因為公式的結果可能會驅動控制項的 X 和 Y 坐標等屬性,因此沒有便利的位置來顯示文字。 每個 Power Fx 主機會控制向終端使用者顯示錯誤的方式,以及製作者對此程序有多少控制權。 在 Power Apps 中,系統會顯示錯誤標語,而 App.OnError 則用來控制錯誤的報告方式。

請特別注意,App.OnError 無法採取與 IfError 相同的方法取代錯誤。 執行 App.OnError 時,已發生錯誤且已透過其他公式傳播結果。 App.OnError 僅控制如何向最終使用者報告錯誤,併為製作者提供一個挂鉤,以便在需要時記錄錯誤。

範圍變數 FirstErrorAllErrors 提供一或多個錯誤的相關背景資訊。 這提供錯誤類型的相關資訊,以及錯誤來源和觀測位置的資訊。

發生錯誤後停止

行為公式支援系統採取動作、修改資料庫以及變更狀態。 這些公式可讓您使用 ; 鏈結運算子 (或 ;;,視地區而定),在一個序列中完成多個動作。

在此情況下,格線控制項顯示的是 T 資料表中的內容。 每個按鈕選取會變更此資料表中的狀態,包含兩個 Patch 呼叫:

動畫顯示資料表 T 中的兩個記錄在每次按一下按鈕後都會以亂數來更新

在鏈結行為公式中,動作不會在第一個錯誤後停止。 讓我們修改範例,在第一個 Patch 呼叫中傳遞無效的索引號碼。 即使先前發生這項錯誤,第二個 Patch 會繼續進行。 系統會將第一個錯誤回報給最終使用者,並在控制項上顯示為 Studio 中的錯誤:

動畫顯示資料表 T 中只有第二個記錄會在每次按一下按鈕後以亂數來更新,第一個記錄則產生錯誤

IfError 可用於在出錯后停止執行。 與 If 函式類似,此函式的第三個引數提供放置只有在無錯誤時才會執行的動作:

動畫顯示資料表 T 的任一記錄皆未發生變更,因為 IfError 讓第二項作業無法在發生錯誤後完成

如果在 ForAll 的其中一個反覆運算期間發生錯誤,則其餘的反覆運算將不會停止。 ForAll 旨在獨立執行每個反覆運算,從而允許並行執行。 ForAll 完成時,系統會傳回錯誤,其中包含所有發生的錯誤 (透過檢查 IfErrorApp.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 範圍變數遇到的所有錯誤。 在此案例中,我們可將此設定為全域變數,並查看是否遇到這兩個錯誤。 這些錯誤會依遭遇順序顯示資料表中。

將錯誤擷取至全域變數 PatchErrors,我們可在其中看到兩個錯誤都出現

系統也可以在非行為公式中傳回多個錯誤。 例如,如果使用的 Patch 函式包含一批需要更新的記錄,系統可能會傳回多個錯誤,表示每個記錄失敗的錯誤。

資料表中的錯誤

如我們先前所見,錯誤可能儲存在變數中。 錯誤也可以包含在資料結構 (例如資料表) 中。 這很重要,因此任何一條記錄上的錯誤都不會使整個資料表無效。

例如,請考慮 Power Apps 中的這個資料表控制項:

資料表顯示輸入為 0 的倒數欄位錯誤,這會導致除以零錯誤

AddColumns 的計算中的其中一個值遭遇除數為零的錯誤。 針對該記錄,Reciprocal 欄有一個錯誤值 (除數為零),但是其他記錄正常且沒有錯誤值。 IsError( Index( output, 2 ) ) 返回 false 並 IsError( Index( output, 2 ).Value ) 返回 true。

如果在篩選資料表時發生錯誤,則整個記錄為錯誤,但是仍會在結果中傳回,進而引起終端使用者注意,而瞭解系統發生問題。

請參考此範例。 在此,原始資料表沒有任何錯誤,但是篩選行為會在 Value 等於 0 時建立錯誤:

資料表顯示因篩選準則無法處理兩個記錄而發生的錯誤

已正確篩除 -5 和 -3 的值。值為 0 會導致處理篩選時出錯,系統不清楚該記錄是否應包含在結果中。 為了盡可能向終端使用者保持透明並協助程式製作者偵錯,我們在原始位置加入錯誤記錄。 在此案例中,IsError( Index( output, 2 ) ) 傳回 true。

資料來源錯誤

修改資料來源中資料的函式,例如 PatchCollectRemoveRemoveIfUpdateUpdateIf,而 SubmitForm 透過兩種方式回報錯誤:

  • 上述每一個函式都傳回錯誤值以作為作業結果。 您可以使用 IsError 偵測錯誤,並使用 IfErrorApp.OnError 進行取代或隱藏。
  • 作業完成後,Errors 函式也會傳回先前作業的錯誤。 這樣即可在表單畫面顯示錯誤訊息,而不需在狀態變數中擷取錯誤。

例如,此公式將會檢查 Collect 是否有錯誤,並顯示自訂錯誤訊息:

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

Errors 函式也會在執行階段作業時,傳回過去錯誤的相關資訊。 這樣即可在表單畫面顯示錯誤,而不需在狀態變數中擷取錯誤。

重新擲回錯誤

有時我們預期系統會發生一些潛在錯誤,並可以安心地忽略這些錯誤。 在 IfErrorApp.OnError 中,如果偵測到應該傳遞給下一個較高處理常式的錯誤,則可透過 Error( AllErrors ) 重新擲回該錯誤。

自行建立錯誤

您也可以使用 Error 函式自行建立錯誤。

如果您要自行建立錯誤,建議您使用大於 1000 的值,以避免可能與未來系統錯誤值發生衝突。

ErrorKind 列舉值

ErrorKind 列舉 數值 名描述
AnalysisError 18 系統錯誤。 編譯器分析發生問題。
BadLanguageCode 14 使用的語言代碼無效或無法識別。
BadRegex 15 無效的規則運算式。 請檢查搭配 IsMatchMatchMatchAll 函式使用的語法。
衝突 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 此記錄未傳遞驗證檢查。