Go
Uitstellen
Zoeken…
Invoering
Een defer
duwt een functieaanroep naar een lijst. De lijst met opgeslagen oproepen wordt uitgevoerd nadat de omringende functie terugkeert. Defer wordt vaak gebruikt om functies te vereenvoudigen die verschillende opruimacties uitvoeren.
Syntaxis
- someFunc (args) uitstellen
- defer func () {// code komt hier} ()
Opmerkingen
Defer werkt door een nieuw stapelframe (de opgeroepen functie na het sleutelwoord defer
) in de oproepstapel onder de momenteel uitvoerende functie te injecteren. Dit betekent dat uitstel gegarandeerd wordt uitgevoerd zolang de stapel wordt afgewikkeld (als uw programma crasht of een SIGKILL
krijgt, wordt uitstel niet uitgevoerd).
Basisbeginselen
Een uitstelinstructie in Go is gewoon een functieaanroep die is gemarkeerd om op een later tijdstip te worden uitgevoerd. Defer-instructie is een gewone functieaanroep voorafgegaan door het sleutelwoord defer
.
defer someFunction()
Een uitgestelde functie wordt uitgevoerd zodra de functie die het bevat defer
verklaring rendement. De daadwerkelijke aanroep van de uitgestelde functie vindt plaats wanneer de omsluitende functie:
- voert een retourverklaring uit
- valt van het einde
- panics
Voorbeeld:
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)
}
Output:
First main statement
Last main statement
Function main returned
Als een functie meerdere uitgestelde instructies heeft, vormen ze een stapel. Het laatste defer
is het eerste dat wordt uitgevoerd nadat de omsluitende functie is teruggekeerd, gevolgd door opeenvolgende aanroepen van voorgaande defer
in volgorde (onderstaand voorbeeld keert terug door paniek te veroorzaken):
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)
}
Output:
First main statement
Num 3
Num 2
Num 1
panic: panic occurred
goroutine 1 [running]:
....
Merk op dat uitgestelde functies hun argumenten laten evalueren op het moment dat defer
uitgevoerd:
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)
}
Output:
First main statement
Num 4
Num 2
Num 1
Als een functie retourwaarden heeft genoemd, kan een uitgestelde anonieme functie binnen die functie toegang krijgen tot de geretourneerde waarde en deze bijwerken, zelfs nadat de functie is geretourneerd:
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
}
Ten slotte worden defer
meestal gebruikt die vaak samen voorkomen. Bijvoorbeeld:
- open en sluit een bestand
- verbinden en ontkoppelen
- vergrendel en ontgrendel een mutex
- een wachtgroep als gereed markeren (wfer.done
defer wg.Done()
)
Dit gebruik zorgt voor een juiste vrijgave van systeembronnen ongeacht de stroom van uitvoering.
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close() // Body will always get closed
Uitgestelde functie-oproepen
Uitgestelde functieaanroepen hebben een soortgelijk doel als dingen als finally
blokken in talen zoals Java: ze zorgen ervoor dat een functie wordt uitgevoerd wanneer de externe functie terugkeert, ongeacht of er een fout is opgetreden of welke return-instructie is getroffen in gevallen met meerdere retouren. Dit is handig voor het opschonen van bronnen die moeten worden afgesloten, zoals netwerkverbindingen of bestandsaanwijzers. Het defer
trefwoord geeft een uitgestelde functie aan te roepen, vergelijkbaar met de go
trefwoord starten van een nieuwe goroutine. Net als een go
call worden functieargumenten onmiddellijk geëvalueerd, maar in tegenstelling tot een go
call worden uitgestelde functies niet tegelijkertijd uitgevoerd.
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
Let op het gebruik van conn.Close()
in plaats van conn.Close
- Je bent niet alleen op doorreis in een functie, je bent het uitstellen van een volledige functie aan te roepen, met inbegrip van haar argumenten. Meerdere functieaanroepen kunnen in dezelfde externe functie worden uitgesteld en elke functie wordt eenmaal in omgekeerde volgorde uitgevoerd. U kunt ook sluitingen uitstellen - vergeet de parens niet!
defer func(){
// Do some cleanup
}()