I would like to run workflow functions using 5 functions in golang
Each method should return the same result object and error object in case of failure
I would like to find a pattern to run this workflow rather than doing the following:
if result, err := init(); err != nil {
if result, err := validate(); err != nil {
if result, err := process(); err != nil {
if result, err := execute(); err != nil {
if result, err := finalize(); err != nil {
}
}
}
}
}
Thanks in advance Peter
You can create a slice of functions and make this whole process prettier, e.g.
functions := []func() (string, error){
function1, function2, function3, function4, function5,
}
for _, function := range functions {
someValue, err := function()
if err != nil {
fmt.Println("Function " + someValue + " didn't feel so good.")
break
}
fmt.Println("Function " + someValue + " went all right!")
}
In short, the break stops things from going on, if you wrap the above process as a function, you could also use something like
return false
in case of an error, and at the end of the total amount of iterations, say i == len(functions) -1, you could return true if everything went all right.
It is noteworthy to mention that you can only create slice of functions that satisfies these conditions:
They all must have the same amount of arguments;
All arguments must have the same type per position;
They all have the same amount of return values;
Likewise, they (return values) must have the same type per position;
Still, you can easily overcome these limitations by having two or three different slices. For example, function1 to function3 can all be classified as
func () (bool, err)
Where function4 and 5 are
func () (int, err)
For these functions to work you could just reiterate the first slice, then if everything goes as planned, move on to the second.
firstSetOfFuncs := []func() (bool, error){
function1, function2, function3,
}
secondSetOfFuncs := []func() (int, err){
function4, function5,
}
for i, function := range firstSetOfFuncs {
someValue, err := function()
if err != nil {
fmt.Println("Something went terribly wrong.")
break
}
if i == len(firstSetOfFuncs) - 1 {
for _, secondTypeOfFunc := range secondSetOfFuncs {
someNum, anotherErr := secondTypeOfFunc()
if anotherErr != nil {
fmt.Println("All seemed all right, until it didn't.")
break
}
}
}
}
As i allready said in a comment, i think the most idiomatic way would be to just call a method, check for errors. And if there was an error return it. You can of course use the simplified error handling version, like you used in your question, but even if you do that. Try to avoid that deep nesting. It's hard to read & understand (deeply) nested code.
result,err := init()
if err != nil {
return nil, err // An error occured so handle it
}
result,err = validate()
if err != nil {
return nil, err
}
result,err = process()
if err != nil {
return nil, err
}
result,err = execute()
if err != nil {
return nil, err
}
result,err = finalize()
if err != nil {
return nil, err
}
You've done the error checking wrong. You are checking for
if err != nil
Spoken "if there is an error". And you continue with your code, although there is an error.
To fix that you need to write:
var result someType
if result, err := init(); err != nil {
return nil, err // An error occured so handle it
}
if result, err := validate(); err != nil {
return nil, err
}
if result, err := process(); err != nil {
return nil, err
}
if result, err := execute(); err != nil {
return nil, err
}
if result, err := finalize(); err != nil {
return nil, err
}