13.1.2 异步下载网页

在我们使用异步工作流来抓取网页内容之前,需要引用 FSharp.PowerPack.dll 库,它包含了许多 .NET 方法的异步版本。开发独立的应用程序时,可以使用添加引用命令;在这一章,我们将使用互动开发模式,因此,创建一个新的 F# 脚本文件,使用 #r 指令(清单  13.1)。

清单13.1 使用异步工作流写代码 (F# Interactive)

> #r "FSharp.PowerPack.dll";;

> open System.IO

open System.Net;;

> let downloadUrl(url:string) = async{     [1]

let request =HttpWebRequest.Create(url)

let! response =request.AsyncGetResponse()     [2]

use response =response     [3]

let stream =response.GetResponseStream()

use reader = newStreamReader(stream)

return!reader.AsyncReadToEnd() };;     [4]

val downloadUrl : string ->Async<string>

导入(opening)所有必需的命名空间以后, 我们定义一个使用异步工作流程的函数,它使用 async 值作为计算生成器[1]。可以轻松地证明,它就是普通的值;在 Visual Studio 中,如果在值的后面键入一个点(.),智能感知会显示它包含的所有常用计算生成器成员,比如,Bind 和 Return,以及其他几个基本操作,我们在以后会要到。输出的类型签名显示,计算类型是 Async<string>。在后面,我们会详细讨论这个类型。

清单 13.1 中的代码,使用一次 let! 结构,执行由 F# 库所提供的异步基本操作 AsyncGetResponse。这个方法返回Async<WebResponse> 类型,因此,let! 结构组合了两个异步操作,把实际的 WebResponse 值绑定到符号 response 上。这样,一旦异步操作完成后,我们就可以使用这个值了。

在下一行[3],使用到了use 基本操作,特定对象一旦超出作用域,就会释放。我们已经讨论过在通常 F# 程序中 use 的使用,它在异步工作流中的行为非常类似。工作流完成时,它会立即释放 HTTP 响应。我们使用值隐藏(value hiding),隐藏原来的 response 符号,声明一个将被释放新值。这是一种常用模式,因此,F# 提供了简便的方法,使用 use! 基本操作,简单地组合了 let! 和 use。既然我们知道了,就可以把上面的两行替换成一行:

use! response = request.AsyncGetResponse()

清单 13.1 的最后一行,我们使用了之前从没见过的基本操作 return![4],它可以运行其他的异步操作(就像使用 let! 基本操作),只是当操作完成时,会返回结果,而不是指定给符号。像 do! 基本操作一样,这也是简单的语法糖(syntactic sugar)。计算生成器不需要实现任何其他的成员,编译器就可以把代码看作是这样写的(实际的转换更简单):

let! text = reader.AsyncReadToEnd()

return text

现在,我们已经有了创建异步计算的 downloadUrl 函数,还应该确定如何用它来下载网页的内容。在清单 13.2 中可以看到,我们使用了 Async 模块中的函数来运行工作流。

清单13.2 进行异步计算 (F# Interactive)

> let downloadTask = downloadUrl("http://www.manning.com");;   [1] <-- 生成异步工作流

val downloadTask : Async<string>

>Async.RunSynchronously(downloadTask);;  [2] <-- 运行工作流,等待结果

val it : string = "<!DOCTYPE htmlPUBLIC "-//W3C//DTD XHTML 1.0

Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-tr

ansitional.dtd"><html><head>(...)"

> let tasks =

[ downloadUrl("http://www.tomasp.net");

downloadUrl("http://www.manning.com") ];;

val tasks : list<Async<string>>

> let all =Async.Parallel(tasks);;   [3] <--把几个工作流组合成一个

val all : Async<string[]>

> Async.RunSynchronously(all);;

val it : string[] = [ "...";"..." ]

使用异步工作流写的代码是自动延迟的,因此,当我们执行第一行的 downloadUrl 函数,它还不会开始下载网页[1]。返回的值(Async<string> 类型)表示想要运行的计算,就像函数值表示以后可以执行的代码一样。Async 模块提供了运行工作流的方法,表 13.1 描述了其中一部分。

表 13.1 在标准 F# 库的 Async 模块中,几个处理异步工作流的基本操作


基本操作


基本操作的类型和描述


RunSynchronously


Async<‘T> –> ‘T

在当前线程中启动给定的工作流。异步操作在工作流中使用时,工作流重新开始线程,用于调用异步回调。此操作会阻塞调用者线程,并等待工作流的结果。


Start


Async<unit> –> unit

在后台(使用线程池线程)启动给定的工作流,并立即返回。工作流与随后的调用者代码并行执行。从签名可知,工作流不返回值。


CreateAsTask


Async<‘T> -> Task<‘T>

这个方法仅在 .NET 4.0 使用。它把异步工作流打包成可用于执行它的 Task<‘T> 对象。任务可以用 Start 或 RunSynchronously 方法启动,其行为类似于 Async 基本操作。要获取工作流的结果,可以使用Result 属性,如果工作流尚未完成,就会阻塞。


Parallel


seq<Async<‘a>> -> Async<array<‘a>>

得到异步工作流的集合,并返回一个工作流,以并行方式执行所有参数值。返回的工作流等待所有操作完成,然后,在一个数组中返回结果。

在清单 13.2 中,我们最初使用 Async.RunSynchronously[2],阻塞了调用线程,这对于以交互方式测试工作流,非常有用。在下一步,我们创建工作流值的列表。另外,在这里,不启动任何运行。我们有了这个集合以后,就可以使用 Async.Parallel 方法[3]生成一个工作流,以并行方式,执行列表中所有工作流。这时,仍不会执行任何原始的工作流;要执行,就需要再次使用 Async.RunSynchronously,以启动组合的工作流,并等待结果。组合的工作流启动所有工作流,并等待所有的工作流都完成。

在等待整体结果时,代码仍会阻塞,但运行的效率高了,它使用 .NET 线程池来平衡运行线程的最大值。如果我们创建几百个任务,它不可能创建几百个线程,因为,这样做效率不高相反,应该使用少量的线程。当工作流使用 let! 结构,进行异步基本操作的调用时,会在系统中注册一个回调,并释放这个线程。因为 .NET 使用线程池管理线程,完成工作的线程可以重用,以启动其他的异步工作流。当我们使用异步工作流时,以并行方式运行的任务数可以远远大于直接使用的线程数。

在本章,我们需要交互地获取数据,因此,我们对以并行方式运行工作流感兴趣的是,而不在意开发响应灵敏的图形应用程序。后一类应用程序(也称为响应式应用程序(reactive applications))也很重要,我们将在第十六章讨论这个主题。现在,我们已经看到了使用异步工作流的代码,下面,就看看它们是如何实现的。

时间: 2024-10-11 19:31:17

13.1.2 异步下载网页的相关文章

GCD异步下载网页功能

最近发现MDT推出去的系统的有不同问题,其问题就不说了,主要是策略权限被域继承了.比如我们手动安装的很多东东都是未配置壮态,推的就默认为安全壮态了,今天细找了一下,原来把这个关了就可以了. GCD异步下载网页功能

UI_18 图片异步下载、KVO

一.异步下载图片ImageViewDownloader 图?下载是iOS开发中常?的功能,但系统并未提供图?下载类. 为了便于后续使?,可以将图?下载封装到?个类?? (ImageDownloader) 新建SingleViewApplication,使用默认的Storyboard,向其中添加一个Button一个ImageView并关联属性.添加事件.点击Button实现异步下载并显示图片.AsynImageDownloader类封装了下载图片的方法. 使用代理进行AsynImageDownlo

javascript异步下载 Promise实现

一般下载都是直接打开一个链接就行.var URL = 'XXXX';window.open(URL)其实这样会有些问题:1. 浏览器禁止打开新窗口,导致无法下载 那么怎么解决呢?这样: 1 <a href="http://somehost/somefile.zip" download="filename.zip">Download file</a> 注意download属性,它说明要下载,并且文件名:filename.zip 那么如何异步下载

13.1.1 异步工作流为什么重要?

假设我们要下载网页的内容,在应用程序中使用,我们可以使用 System.Net 命名空间下的 WebClient 类,但下不能演示我们需要解决的问题,运行复杂.长时间的操作.相反,我们将首先显式创建 HTTP 请求,然后,下载数据: var req =HttpWebRequest.Create("http://manning.com"); var resp = req.GetResponse();    <-- 初始化连接 var stream = resp.GetRespons

iOS异步下载下载进度条显示

说到http异步下载,首先要知道其中的关键类. 关键类是NSURLConnection  NSURLRequest NSMutableURLRequest  委托是 NSURLConnectionDownloadDelegate NSURLConnectionDataDelegate NSURLConnectionDelegate 首先,我们要实现最基本的下载功能. LQAsynDownload.h // // LQAsynDownload.h // lgTest // // Created b

C#使用BeginInvoke和EndInvoke异步下载和获取返回结果

场景:为了防止UI卡死,使用异步下载文件 问题:采用多线程下载,关闭窗口后下载线程不能停止,线程操作麻烦. 参考:C#客户端的异步操作: http://www.cnblogs.com/fish-li/archive/2011/10/23/2222013.html 方案:采用BeginInvoke的方式调用下载方法,委托会自动启动新线程,停止时也不需要手动控制.使用EndInvoke获取返回结果. try { IAsyncResult ir = process.BeginInvoke(new Ht

表情包的同步异步下载

同步下载 from lxml import etree import requests from urllib import request #保存图片 import os import re def parse_page(url): headers = { "User-Agent":"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/5

android开发步步为营之67:使用android开源项目android-async-http异步下载文件

android-async-http项目地址 https://github.com/loopj/android-async-http,android-async-http顾名思义是异步的http请求,不过它也支持同步请求的,本文主要使用了android-async-http做异步请求下载文件.每当app需要更新新版本的时候,就需要用到下载功能的,经研究android-async-http这个第三方开源项目还是挺好用的,这里介绍给大家.     1.下载类DownloadManager.java

c# 下载网页图片

也是比较老的东西了 最近用到 记录下以免以后忘了 要下载图片首先要有图片地址 要有图片地址就要先把网页下下来分析下URL 下载网页一般用两种方法 1,用 system.net.webclient using System.Net; using System.Windows.Forms; string url = "http://www.cnblogs.com"; string result = null; try { WebClient client = new WebClient()