We’ve recently introduced a new error-handling mechanism using the new throw() command.
This marked the first step towards a new error handling system, positioned as close as possible to the code generating them.
In the next phase with 4D v20 R4, we’re expanding this approach to intercept errors during expression execution.
Let’s delve into the details.
try(), a new helpful keyword
With the introduction of 4D v20 R4, developers can now access a useful new keyword: Try(Expression). The Try() keyword enables developers to execute an expression enclosed in parentheses and seamlessly intercept any errors thrown during its execution. These errors can be handled using the Last errors command immediately after the expression execution. This allows you to define your error management as close as possible to the code that generated them and eventually use local variables.
Sample Scenarios before detailed explanations
Division by zero
Consider a simple scenario: division by zero. The following sample demonstrates an Euclidean division method that throws an error and arbitrarily returns zero if the divisor is zero. With Try(), developers can manage errors more elegantly:
#DECLARE($dividend : Real; $divisor : Real)->$result : Real
If ($divisor=0)
$result:=0
throw(-12345; "Division by zero!")
Else
$result:=($dividend/$divisor)
End if
$result:=Try(division($dividend; $divisor))
If (Last errors#Null)
// Error management
logErrors(Last errors)
End if
ACCESSING a Document
Managing errors when accessing a document becomes more straightforward if the document doesn’t exist.
To prevent the 4D error dialog from being displayed, an error handler needed to be installed, and the error management code had to be written within this error handler:
$previousErrorHandler:=Method called on error
ON ERR CALL("errorOpenDoc")
var $fileHandle : 4D.FileHandle:=File($path).open()
If ($fileHandle#Null)
ON ERR CALL("errorReadDoc")
$text:=$fileHandle.readText()
If (Last errors#Null)
$text:="Error reading the file"
End if
End if
ON ERR CALL($previousErrorHandler)
With the Try() keyword, everything can be written in the same section of code, accessing local variables and then simplifying readability:
var $fileHandle : 4D.FileHandle:=Try(File($path).open())
If ($fileHandle#Null)
$text:=Try($fileHandle.readText()) || "Error reading the file"
End if
ORDA save function
Another robust use case involves the ORDA save() function, which can generate predictable and unpredictable errors.
Predictable errors, like entity lock, can be directly handled through the result success attribute. However, unpredictable errors, such as primary key duplicates, required an error handler installed with the ON ERR CALL command; otherwise, the 4D error dialog would be displayed:
var $customer : cs.CustomerEntity
// Do something with the $customer entity
$previousErrorHandler:=Method called on error
ON ERR CALL("errorSaveManagement")
$result:=$customer.save()
ON ERR CALL($previousErrorHandler)
If (Not($result.success))
Case of
: ($result.status=3)
// Locked entity management
: ($result.status=4)
// Other error management
End case
End if
But now, the Try() keyword becomes a valuable ally in handling both predictable and unpredictable errors without showing the 4D error dialog:
var $customer : cs.CustomerEntity
// Do something with the $customer entity
$result:=Try($customer.save())
If (Not($result.success))
Case of
: ($result.status=3)
// Locked entity management
: ($result.status=4)
// Other error management
End case
End if
Expression Execution
The Try() keyword accepts any 4D expression between the parentheses, be it variables, assignments, methods, functions, or more. In case of an error, the execution flow stops and returns to the latest Try() keyword encountered (the first found back in the call stack). As a result, you just have to check the error stack after the expression execution to know if errors occurred.
Note that if an error is generated using the throw() command in deferred mode, the execution flow continues until the end of the current method/function execution, providing a simple way to manage error propagation.
Results
On successful execution, Try() returns the result of the expression.
In case of an error, it returns the latest result of the expression or ‘undefined’ if unavailable.
If the expression defines no result (e.g., a method or command with no result), the try() keyword returns ‘undefined.’
About the Error Stack
The current error stack is cleared before the expression is executed, offering a clean slate for error handling within the Try() context. Errors encountered during expression execution are added to the error stack, providing developers with comprehensive information for debugging.
Error Handling
If no current error handler is defined using the ON ERR CALL command, the error dialog won’t disrupt the user experience using Try().
Global and local error handlers set before entering the Try() block won’t be called, ensuring a clean separation between the try context and the rest of the code.
If the expression defines a global or local error handler for the current database, it will be invoked when an error occurs during execution.
Explore New Horizons in Error Management
This new feature opens up a world of possibilities for error management. But it’s only the second step in the new 4D error management era that opens the way to multi-line try-catch blocks!
Share your thoughts and experiences on our forum, and let us know what you think about this new feature.
Happy coding!