13.1.3 理解工作流的工作方式

13.1.3 理解工作流的工作方式



在前一章中,我们看到,用计算表达式写的 F# 代码被转换成一个表达式,它使用由适当计算生成器提供的基元。对于异步工作流,这意味着,let! 结构被转换成对 async.Bind 的调用,return 被转换成 async.Return。此外,异步工作流是延迟的。这意味着,计算本身要打包到一个额外的基元,确保整个代码括在一个函数中。这个函数在后面工作流启动时执行。清单 13.3 显示从清单 13.2 工作流的转换后的版本。



Listing 13.3 Asynchronous workflow constructed explicitly (F#)



async.Delay(fun () –>
let request = HttpWebRequest.Create(url)
async.Bind(request.AsyncGetResponse(), fun response –>
async.Using(response, fun response –>
let stream = response.GetResponseStream()
async.Using(new StreamReader(stream), fun reader –>
reader.AsyncReadToEnd() )
)
)
)



Delay 成员把一个函数打包到一个工作流值,可以在以后执行。Lambda 函数体作为一全参数值,创建一个 HTTP 请求,使用自定义的异步值绑定,把一个值赋给 resp 符号。编译器把每个使用的绑定转换成对 Using 成员的调用,这是另一种基元,可以通过计算表达式生成器可选地提供的。它负责在工作流结束时,释放对象,不管成功,还是有错误。

Delay 成员是计算生成器成员之一,当实现计算表达式时,我们可以提供。在清单 13.3 中,它取一个返回异步工作流 (类型是 unit ->Async<’a>)的函数,并返回工作流值(Async<’a>),它打包了这个函数。由于有了这个基元,整个计算被括在一个函数之内,它并不在我们创建 Async<’a> 值时执行。这是与前一章的例子的一个重要区别,比如,option<'a> 类型。一个选项表示一个值,所以,计算表达式立即运行,执行这个计算,并返回一个新的选项值,只是工作流表示计算。当我们详细地看过 Async<’a> 类型之后,其意思主就会变得更清晰。

出现在清单 13.3 中的其他基元类型是 Bind 成员。就像我们在前一章中学习的,这对于所有的计算表达式是很重要的。在异步工作流中,Bind 可以启动一个操作,而不会阻塞调用者线程。下面的列表概述了当我们使用基元,比如:Async.RunSynchronously,执行工作流时发生的步骤:

1、作为参数值给 Delay 基元的函数启动执行。它同步创建一个对象,表示对给定的 URL 的 HTTP 请求。

2、调用 AsyncGetResponse。结果是一个基元的异步工作流,知道如何启动请求,当操作完成时调用指定的函数。

3、我们执行这个 Bind 成员,并把在第二步中的工作流作为第一个参数值给它,再给它一个函数,这个函数取 HTTP 响应作为一个参数值,它应该在工作流完成时执行。这个函数被称为连续(continuation),这是我们已经看到过的一个术语。(我们在第 10 章讨论递归时,使用过,是作为一个累加器参数,为了累加希望在以后运行的更多代码的。)

4、这个 Bind 成员运行由 AsyncGetResponse 创建的工作流,向它传递指定的连续。基元工作流然后调用 .NET 的 BeginGetResponse 方法,指示系统启动下载响应,当操作完成时调用给定连续。在这点,Bind 成员返回,正在执行操作的线程被释放,继续做其它工作,或者它被返回给线程池。

5、当响应准备就绪时,系统将调用这个连续。工作流使用 EndGetResponse 的 .NET 方法获取响应对象,执行给 Bind 成员的,表示工作流的其余部分。注意,系统再从线程池取一个线程,所以,每次我们使用 let! 基元时,工作流的其余部分可能在不同的线程上执行。

关键的一点是,当我们执行异步工作流时,不要等待结果。相反,我们把连续给它作为参数值;当工作流中的相应步骤完成时,这个连续就执行。有关异步工作流的主要事情是,我们不必显式写使用连续的代码。编译器把 let! 基元转换成对 Bind 成员的调用,自动创建连续。



研究异步工作流的类型



我们可以使用异步工作流,而不必了解所有的详细信息,但是,你对它们如何实现可能有点兴趣。我们已经看到,异步工作流类似于函数,因为,它们表示的计算可以在以后执行。在 F# 库中,类型用函数表示。类型有点复杂,但是,最简单的异步计算可以使用下面的代码来表示:



type Async<'T> = (('T -> unit) * (exn -> unit) * (unit -> unit)) –> unit



这是一个函数,它取三个参数值,作为一个元组,并返回一个 unit 值。这三个参数值是重要的,因为它们是连续-函数,可以在异步工作流完成时调用。第一个被称为成功连续,其类型是 'T -> unit,这就意味着,它取这个工作流的结果。当这个工作流完成时,连续将调用。然后,它可以运行另一个工作流,或者任何其他代码。第二个是异常的连续,它取 exn 值作为参数值,这是 F# .NET Exception 类型的缩写。如你所猜想的,它使用在工作流执行的操作失败时。第三个函数称为取消连续,工作流被取消时可以当触发。



虽然异步工作流的精确实现细节并不是必要的,能够创建自己的基元工作流,它很有用,相当于清单 13.3 中的使用 AsyncGetResponse 方法。然后,可以使用构造块的其余部分,用最小的烦恼来异步运行自己的代码。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值