Buscar..
Introducción
Una declaración defer
empuja una llamada de función a una lista. La lista de llamadas guardadas se ejecuta después de que vuelve la función que la rodea. El aplazamiento se usa comúnmente para simplificar las funciones que realizan varias acciones de limpieza.
Sintaxis
- diferir someFunc (args)
- aplazar func () {// el código va aquí} ()
Observaciones
La función de defer
inyecta un nuevo marco de pila (la función llamada después de la palabra clave de defer
) en la pila de llamadas debajo de la función que se está ejecutando actualmente. Esto significa que se garantiza que el aplazamiento se ejecute siempre que la pila se desenrolle (si su programa falla o obtiene un SIGKILL
, el aplazamiento no se ejecutará).
Diferir lo básico
Una declaración diferida en Go es simplemente una llamada de función marcada para ejecutarse en un momento posterior. La declaración diferida es una llamada de función ordinaria prefijada por la palabra clave defer
.
defer someFunction()
Una función diferida se ejecuta una vez la función que contiene los defer
retorne el comando. La llamada real a la función diferida se produce cuando la función de cierre:
- ejecuta una declaración de retorno
- se cae del final
- pánico
Ejemplo:
func main() {
fmt.Println("First main statement")
defer logExit("main") // position of defer statement here does not matter
fmt.Println("Last main statement")
}
func logExit(name string) {
fmt.Printf("Function %s returned\n", name)
}
Salida:
First main statement
Last main statement
Function main returned
Si una función tiene varias declaraciones diferidas, forman una pila. El último defer
es el primero que se ejecuta después de que se devuelve la función de cierre, seguido de las llamadas subsiguientes a los defer
anteriores en orden (a continuación, el ejemplo devuelve causando un pánico):
func main() {
defer logNum(1)
fmt.Println("First main statement")
defer logNum(2)
defer logNum(3)
panic("panic occurred")
fmt.Println("Last main statement") // not printed
defer logNum(3) // not deferred since execution flow never reaches this line
}
func logNum(i int) {
fmt.Printf("Num %d\n", i)
}
Salida:
First main statement
Num 3
Num 2
Num 1
panic: panic occurred
goroutine 1 [running]:
....
Tenga en cuenta que las funciones diferidos han evaluado sus argumentos en el momento defer
ejecuta:
func main() {
i := 1
defer logNum(i) // deferred function call: logNum(1)
fmt.Println("First main statement")
i++
defer logNum(i) // deferred function call: logNum(2)
defer logNum(i*i) // deferred function call: logNum(4)
return // explicit return
}
func logNum(i int) {
fmt.Printf("Num %d\n", i)
}
Salida:
First main statement
Num 4
Num 2
Num 1
Si una función ha nombrado valores de retorno, una función anónima diferida dentro de esa función puede acceder y actualizar el valor devuelto incluso después de que la función haya regresado:
func main() {
fmt.Println(plusOne(1)) // 2
return
}
func plusOne(i int) (result int) { // overkill! only for demonstration
defer func() {result += 1}() // anonymous function must be called by adding ()
// i is returned as result, which is updated by deferred function above
// after execution of below return
return i
}
Finalmente, una sentencia de defer
generalmente se usa en operaciones que a menudo ocurren juntas Por ejemplo:
- abrir y cerrar un archivo
- conectar y desconectar
- bloquear y desbloquear un mutex
- marcar un grupo de espera como hecho (
defer wg.Done()
)
Este uso garantiza la liberación adecuada de los recursos del sistema independientemente del flujo de ejecución.
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close() // Body will always get closed
Llamadas de función diferida
Las llamadas a funciones diferidas tienen un propósito similar a cosas como los bloques finally
en lenguajes como Java: aseguran que alguna función se ejecutará cuando se devuelva la función externa, independientemente de si se produjo un error o qué declaración de devolución se golpeó en casos con múltiples devoluciones. Esto es útil para limpiar recursos que deben cerrarse como conexiones de red o punteros de archivos. La palabra clave defer
indica una llamada a función diferida, de manera similar a la palabra clave go
inicia una nueva goroutina. Al igual que una llamada go
, los argumentos de la función se evalúan inmediatamente, pero a diferencia de la llamada go
, las funciones diferidas no se ejecutan simultáneamente.
func MyFunc() {
conn := GetConnection() // Some kind of connection that must be closed.
defer conn.Close() // Will be executed when MyFunc returns, regardless of how.
// Do some things...
if someCondition {
return // conn.Close() will be called
}
// Do more things
}// Implicit return - conn.Close() will still be called
Tenga en cuenta el uso de conn.Close()
lugar de conn.Close
: no solo está pasando una función, está aplazando una llamada de función completa, incluidos sus argumentos. Las múltiples llamadas de función se pueden diferir en la misma función externa, y cada una se ejecutará una vez en orden inverso. También puede aplazar los cierres, ¡no se olvide de los parens!
defer func(){
// Do some cleanup
}()