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
}()


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow