在这个线程中有一句话,根据 async / await 模型处理事件比传统的基于事件的方法有很多优势 - 订阅和忘记。
从理论上讲,您可以在没有 async/await 的情况下生活。但是随后您将代码分散在事件处理程序中,并且状态将采用全局变量的形式。而且我什至没有谈论异常处理——这将非常困难。但是,是的,不知何故,人们以前没有 async / await 生活,但仍然生活在其他语言中。
事实上,它让我感兴趣。
我想通过示例、传统方法和 async / await 方法获取有关所有优点和可能缺点的信息。
假设我们有一个 UI 应用程序的任务:执行一些逻辑,然后显示一个视图,等到该视图关闭并执行其他操作。如何以传统方式解决它(我使用 Window 作为视图纯粹是为了简化示例,在实际任务中,任何东西都可以代替窗口):
注意3点:
正如你所看到的,如果你想解决一个看似简单的问题,你必须躲避让代码按照你想要的方式工作。但是让我们编写一个更易于使用的窗口:
如您所见,窗口现在有一个返回任务的方法。只有当窗口关闭时,此任务才会结束。现在我们可以像这样重写调用代码:
不再摆弄框架或代表或任何东西。
这只是您可以在没有 async/await 的情况下使用的简单示例之一,但是使用 async/await 会使代码更加简洁和易于理解。您可以将窗口替换为任何内容(从网络获取数据、写入数据库、任何异步调用),该窗口仅作为使用
TaskCompletionSource.看。传统的异步工作方式是使用回调。在执行此操作的每一点,您都
await必须通过订阅异步代码的末尾来结束该方法。这样做时,您必须将状态保存在某个地方,也就是说,您必须手动携带局部变量。此外,循环和条件的逻辑也被证明分布在几段代码中。好吧,您将不得不手动划分为必要的部分。这是一个简单的异步代码示例:复制流。
没有什么特别的。
我们如何在不占用线程的情况下编写相同的功能而无需同步代码?我们有一个方法
stream.BeginRead应该返回一个类型为 的对象IAsyncResult。让我们尝试以相同的方式对我们的功能进行建模。首先,我们需要在某个地方存储缓冲区以及工作线程。为此,我们需要一个类。让我们称之为
StreamCopyWorker,记住工作的逻辑也将在其中。接下来,我们要定义IAsyncResult. 让我们将它声明为一个单独的类,因为它仍然是一个单独的实体。StreamCopyWorker必须有方法BeginCopyAsync和EndCopyAsync. 实施。原来是这样一条鳄鱼:好吧,调用辅助方法来隐藏类的创建:
没有 async/await 对你来说仍然很容易吗?
使用纯事件模型,意大利面条就更糟了。现在你可以通过闭包来拉取状态,这稍微简化了代码。但不要太多。我对纯事件有相同的任务,看起来像这样:
(加上
ResultCallback,ReadHandler,WriteHandler等的定义ResultArgs)。你肯定在 Javascript 代码中看到过类似的,只是更大的死亡金字塔。你明白这段代码发生了什么吗?我走了。