En este post se explica cómo usar defer en Go, así como sus ventajas e importancia.
El lenguaje de programación Go provee una característica muy especial y útil a la vez: defer
. Esta sentencia ayuda mucho a la hora de programar con Go, pues permite ejecutar alguna sentencia al finalizar o terminar el flujo de una función, sin importar en qué punto se regrese.
Al principio suena complicada, pero su uso es realmente sencillo y no estamos obligados a usarlo si no queremos. De hecho se podría decir que es azúcar sintáctica del lenguaje.
Esta sentencia ejecuta el código que le indicamos antes de que se termine la función, sin importar en qué punto se termine la misma. Un ejemplo básico es el siguiente (tomado de aquí):
package main
import "fmt"
func main() {
defer fmt.Println("Mundo")
fmt.Println("Hola")
}
Ese código imprimirá:
Hola
Mundo
En la primera línea se pone a defer fmt.Println("Mundo")
y en la segunda fmt.Println("Hola")
. Aunque parece que se imprimirá Hola y luego Mundo, el resultado es lo contrario.
Dicho de otra manera, el código de arriba es lo equivalente a escribir esto:
package main
import "fmt"
func main() {
fmt.Println("Hola")
fmt.Println("Mundo")
}
La sentencia defer en Go mueve el código al final de la función, si se quiere ver así, aunque en realidad lo ejecuta antes de que la función se termine, sin importar el punto en donde se hace el return
.
Veamos este programa que si bien no tiene mucho sentido, ejemplifica el uso de defer. Ya lo dije, defer
permite que se ejecute código al terminar la función, sin importar en qué punto se termine.
package main
import "fmt"
func main() {
fmt.Println("Escribe tu edad:")
var edad int
fmt.Scanln(&edad)
// Al final, siempre mostrar este mensaje
defer fmt.Println("Gracias. Vuelva pronto")
if edad < 18{
fmt.Println("No puedes acceder al mensaje secreto")
return//Termina la función
}
fmt.Println("Hola, soy un mensaje que se muestra si tienes más de 18 años")
}
Leemos a través del teclado la edad del usuario, si su edad es menor a 18, le indicamos que no puede ver el contenido y terminamos el flujo de la función con return
.
En caso de que tenga más de 18 años, le mostramos un mensaje secreto.
Lo que quiero resaltar aquí, es que sin importar lo que el usuario escriba, al final se muestra el mensaje que dice que vuelva pronto. No importa si la función termina normalmente o “se interrumpe”, todo lo que esté agendado con defer
se ejecutará antes de que la función se termine.
La sentencia defer en Go es un poco parecida a la sentencia finally de un try/catch que otros lenguajes de programación tienen; con la gran diferencia de que en Go no hay excepciones, pero esa es otra historia.
Recordemos que finally se ejecuta sin importar si se cae dentro del catch.
Vamos a analizar las ventajas que tiene defer en Go, porque esta sentencia, que yo sepa, no existe en los lenguajes de programación más populares.
Cuando usamos defer
, podemos terminar algo que estamos iniciando, pocas líneas después de lo primero. Por ejemplo, tomemos el ejemplo real en donde conectamos a Go con MySQL. Un fragmento de código para abrir la base de datos dice así:
db, err := obtenerBaseDeDatos()
if err != nil {
fmt.Printf("Error obteniendo base de datos: %v", err)
return
}
// Terminar conexión al terminar función
defer db.Close()
// Aquí 10 millones de líneas...
Como ya abrimos la base de datos, indicamos que antes de salir de la función actual, la misma se cierre con db.Close()
. No importa si abajo hay 10 millones de líneas, la base de datos se cerrará cuando se termine.
En un futuro cuando nosotros u otro programador lea el código, sabrá que la base de datos que se está abriendo, se está cerrando al final; sin importar en dónde.
Además, si más abajo hay múltiples condiciones que terminan la función, no tenemos que ir dentro de cada una de ellas y poner db.Close()
en caso de que la función no siga su flujo normal.
El mismo ejemplo de arriba. Si hubiera muchas líneas de código y no existiera defer
, a menudo olvidaríamos terminar lo que empezamos.
El ejemplo fue con una base de datos, pero lo mismo pasa con un archivo y cualquier otra cosa que se deba terminar cada que se empieza.
Solamente queda mencionar que se puede llamar a defer las veces que sea necesario. Cuando eso pase, las sentencias se ejecutan en el orden inverso de como fueron llamadas.
Un ejemplo sencillo es el siguiente, en donde imprimimos 4 mensajes, 3 de ellos con defer. Al final, los mensajes se imprimirán ordenados.
package main
import "fmt"
func main() {
// Normal
fmt.Println("Hola, mundo")
// Se ejecutarán al revés
defer fmt.Println("parzibyte.me")
defer fmt.Println("defer desde")
defer fmt.Println("probando")
}
Esto es porque la primera llamada a defer se ejecuta al final, la segunda antes del final, y así sucesivamente. El orden es LIFO: último en entrar, primero en salir.
Así es como funciona defer en Go.
Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…
En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…
En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…
Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…
En este artículo te voy a enseñar cómo usar un "top level await" esperando a…
Ayer estaba editando unos archivos que son servidos con el servidor Apache y al visitarlos…
Esta web usa cookies.
Ver comentarios
Muy buena entrega, solo algo en tu primer ejemplo en el de Hola Mundo, lo tienes cambiado, para que te quede perfecto 👌