Go语言的defer算是一个语言的新特性,至少对比当今主流编程语言如此。根据GO LANGUAGE SPEC的说法:git
A "defer" statement invokes a function whose execution is deferred to the moment the surrounding function returns, either because the surrounding function executed a return statement, reached the end of its function body, or because the corresponding goroutine is panicking. defer语句调用一个函数,这个函数执行会推迟,直到外围的函数返回,或者外围函数运行到最后,或者相应的goroutine panicgithub
换句话说,每当defer执行的时候,它后面的函数值(在go中函数是一个引用类型,是一等公民,能够赋值给变量)和函数参数会被求值,可是函数不会当即调用,直到(↑)上述三种状况发生。 这就是defer的所有内容,没了,剩下就是defer的best practice编程
先从最简单的开始:编程语言
func readFile(fileName string){ f,err := os.Open(fileName) if err!=nil { return } defer f.Close() var content [1024]byte f.Read(content[:]) fmt.Printf("%s",content) } func main() { readFile("test.data") }
程序输出test.data前1024字节的内容。值得一提的是,相似这种open/close配对操做是defer的惯用法。 这个例子诠释了上面那句话的后半段函数
"可是函数不会被调用" spa
由于若是defer后面的f.Close()
没有延迟执行,那么文件描述符都关闭了,就不会读取到任何内容。code
下面这个例子即将诠释上半段,它来自<>,稍做修改:blog
func trace(funcName string) func(){ start := time.Now() fmt.Printf("function %s enter\n",funcName) return func(){ log.Printf("function %s exit (elapsed %s)",funcName,time.Since(start)) } } func foo(){ defer trace("foo()")() time.Sleep(5*time.Second) } func main(){ foo() foo() } /* OUTPUT: function foo() enter function foo() exit (elapsed 5.0095471s) function foo() enter function foo() exit (elapsed 5.0005382s) */
为何foo会输出enter
而后等待五秒左右再输出exit
? 由于正如咱们说的,字符串
defer后面的函数值和参数会被求值可是实际函数调用却要等到最后get
这里函数值就是trace()
返回的匿名函数,函数参数固然就是字符串字面值"foo()", 对trace("foo()")
的求值会输出function foo() enter
, 实际函数调用trace("foo()")()
即输出function foo() exit(elapsed x.x)
会推迟到return执行(若是return会更新返回值变量,则会在更新后才执行defer的函数)。
多说一点,若是存在多个defer语句,最后的defer的函数的执行顺序与defer出现的顺序相反,如:
func main() { func1 := func(){ fmt.Println("func1() execution deferred") } func2 := func(){ fmt.Println("func2() execution deferred") } defer func1() defer func2() fmt.Println("strat\nworking...") } /* OUTPUT: strat working... func2() execution deferred func1() execution deferred */