使用 Go 实现 Async/Await 模式
概述
Golang 是一种并发编程语言。它具有强大的特性,如 Goroutines
和 Channels
,可以很好地处理异步任务。另外,goroutines
不是 OS 线程,这就是为什么您可以在不增加开销的情况下根据需要启动任意数量的 goroutine
的原因,它的堆栈大小初始化时仅 2KB。那么为什么要 async/await
呢?Async/Await
是一种很好的语言特点,它为异步编程提供了更简单的接口。
项目链接:https://github.com/icyxp/AsyncGoDemo
它是如何工作的?
从 F# 开始,然后是 C#,到现在 Python 和 Javascript 中,async/await
是一种非常流行的语言特点。它简化了异步方法的执行结构并且读起来像同步代码。对于开发人员来说更容易理解。让我们看看 c# 中的一个简单示例 async/await
是如何工作的。
当程序运行时,我们的 Main
函数将被执行。我们有异步函数 DoneAsync
。我们使用 Delay
方法停止执行代码 3 秒钟。Delay 本身是一个异步函数,所以我们用 await
来调用它。
await
只阻塞异步函数内的代码执行
在 Main
函数中,我们不使用 await
来调用 DoneAsync
。但 DoneAsync
开始执行后,只有当我们 await
它的时候,我们才会得到结果。执行流程如下所示:
对于异步执行,这看起来非常简单。让我们看看如何使用 Golang 的 Goroutines
和 Channels
来做到这一点。
在这里,DoneAsync
异步运行并返回一个 channel
。执行完异步任务后,它会将值写入 channel
。在 main
函数中,我们调用 DoneAsync
并继续执行后续操作,然后从返回的 channel
读取值。它是一个阻塞调用,等待直到将值写入 channel
,并在获得值后将其输出到终端。
我们看到,我们实现了与 C# 程序相同的结果,但它看起来不像 async/await
那样优雅。尽管这确实不错,但是我们可以使用这种方法轻松地完成很多细粒度的事情,我们还可以用一个简单的结构和接口在 Golang 中实现 async/await
关键字。让我们试试。
实现 Async/Await
完整代码可在项目链接中找到(在文章开始的地方)。要在 Golang 中实现 async/await
,我们将从一个名为 async
的包目录开始。项目结构看起来是这样的。
在 async
文件中,我们编写了可以处理异步任务最简单的 Future
接口。
这里发生的事情并不多,我们添加了一个具有 Await
方法标识的 Future
接口。接下来,我们添加一个 future
结构,它包含一个值,即 await
函数的函数标识。现在 futute struct
通过调用自己的 await
函数来实现 Future
接口的 Await
方法。
接下来在 Exec
函数中,我们在 goroutine
中异步执行传递的函数。然后返回 await
函数。它等待 channel
关闭或 context
读取。基于最先发生的情况,它要么返回错误,要么返回作为接口的结果。
现在,有了这个新的 async
包,让我们看看如何更改当前的 go 代码:
乍一看,它看起来干净得多,这里我们没有显式地使用 goroutine
或 channels
。我们的 DoneAsync
函数已更改为完全同步的性质。在 main
函数中,我们使用 async
包的Exec
方法来处理 DoneAsync
。在开始执行 DoneAsync
。控制流返回到可以执行其他代码的 main
函数中。最后,我们对 Await
进行阻塞调用并回读数据。
现在,代码看起来更加简单易读。我们可以修改我们的 async 包从而能在 Golang 中合并许多其他类型的异步任务,但在本教程中,我们现在只坚持简单的实现。
结论
我们经历了 async/await
的过程,并在 Golang 中实现了一个简单的版本。我鼓励您进一步研究 async/await
,看看它如何更好的让代码库便于易读。
译自:https://hackernoon.com/asyncawait-in-golang-an-introductory-guide-ol1e34sg
版权声明: 本文为 InfoQ 作者【Roc】的原创文章。
原文链接:【http://xie.infoq.cn/article/78d73ca8b1fe019e0f629f8ff】。文章转载请联系作者。
评论 (1 条评论)