RxJS入门(4)----深入Sequence

来源:互联网 发布:哈工大大数据集团 国企 编辑:程序博客网 时间:2024/06/13 10:33

接着(3),这一张分成两部分来翻译。

Making a Real-Time Earthquake Visualizer

  • 使用在本章中所涉及到的概念,我们将使用Rxjs来创建一个web应用程序,来展现那些物理时间上正在发生的地震的地方。我们将会以建立一个单纯响应的实现功能为开始,随着我们的深入,我们将会改进它,最后的结果将会如下所示:
    这里写图片描述
  • 准备我们的环境
  • 我们将会使用USGS(U.S. Geological Survey,美国地质调查局)的地震数据库,它提供了若干种格式的真实地震数据库。我们将会以JSONP的格式从每周数据集中获取数据。
  • 我们将会使用Leaflet,一个JavaScript库,来渲染交互式地图。让我们瞅瞅我们的index.html长啥样,并重温下重点:
<!DOCTYPE html><html lang="en-us"><head><meta charset="utf-8"><link rel="stylesheet"href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css" /><script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script><script src="../rx.all-4.0.0.js"></script><title>Earthquake map</title><style type="text/css">html, body {margin: 0;padding: 0;height: 100%;}#map { height: 100%; }</style></head><body><div id="map"></div><script>var QUAKE_URL = 'http://earthquake.usgs.gov/earthquakes/feed/v1.0/' +'summary/all_day.geojsonp';❷ function loadJSONP(url) {var script = document.createElement('script');script.src = url;var head = document.getElementsByTagName('head')[0];head.appendChild(script);}❸ var map = L.map('map').setView([33.858631, -118.279602], 7);❹ L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png').addTo(map);</script><script src="code.js"></script></body></html>

上面代码1~5标签的说明如下;

  1. 这是使用Leaflet来渲染地图的div占位符。
  2. 这是我们用来加载JSONP内容的工具函数。
  3. 我们在合理的缩放级别上设置洛杉矶(这里地震比较多)的中心坐标来初始化Leaflet地图。
  4. 我们告诉Leaflet为我们的地图设置默认的主题集。这些主题集仅仅是“皮肤”对我们的地图来说。
  5. 最后,在DOM和地图初始化后加载我们的代码。

检索地震的位置

  • 现在我们的HTML已经准备好了。我们可以开始偶们程序的逻辑了。首先,我们需要知道那种格式的数据我得到的,并且什么样的数据我们需要来表现地图上地震的。
  • USGS站点给我们返回来的JSONP格式是如下的:
eqfeed_callback({"type": "FeatureCollection","metadata": {"generated": 1408030886000,"url": "http://earthquake.usgs.gov/earthquakes/...","title": "USGS All Earthquakes, Past Day","status": 200, "api": "1.0.13", "count": 134},"features": [{"type": "Feature","properties": {"mag": 0.82,"title": "M 0.8 - 3km WSW of Idyllwild-Pine Cove, California","place": "3km WSW of Idyllwild-Pine Cove, California","time": 1408030368460,...},"geometry": {"type": "Point","coordinates": [ -116.7636667, 33.7303333, 17.33 ]},"id": "ci15538377"},...]})
  • 这个功能数组包含一个对象,这个对象中的数据是今天发生的每一个地震。这是一个大容量的数据。这是很令人震惊和害怕的,为啥一天就有这么多的抵账发生。在我们的程序中,我们仅仅需要每个地震的坐标,主题和量级。
  • 我们首先想要创建一个Observable,它获取数据集,并且发射单个的地震,下面就是第一个版本:
var quakes = Rx.Observable.create(function(observer) {window.eqfeed_callback = function(response) {var quakes = response.features;quakes.forEach(function(quake) {observer.onNext(quake);});};loadJSONP(QUAKE_URL);});quakes.subscribe(function(quake) {var coords = quake.geometry.coordinates;var size = quake.properties.mag * 10000;L.circle([coords[1], coords[0]], size).addTo(map);});
  • 等等,我们代码中的赤裸裸的全局函数window.eqfeed_callback正在做什么?噢,它是JSONP URLs经常提供的一种途径:以在url中增加查询串到指定的函数名去处理回来的响应,但是USGS站点不允许那样,所以我们需要使用eqfeed_callback这个他们决定的名字来创建一个全局函数。
  • 我们的Observable按顺序发射所有的地震信息。现在我们就相当于有了一个的地震信息生成器了。我们没有必要关系异步的流,可以把所有的逻辑都放到一个同样的函数中。一旦我们订阅了这个Observable,地震信息就会被发送给我们。
  • 通过检索地震Observable的“黑盒子”,我们现在可以订阅它,并且处理每一个地震信息。之后,我们就根据每一个的地震的量级成比例的画一个圈。
  • 更深入点
  • 我们可以做得更好点吗?你可以的!在上面的代码中,即使在Observable中的都是隔离的,我们依然假设流通过传送数组和调用onNext来产生每一个地震信息。这有如此多的交互。
  • 补充:什么是JSONP
  • JSONP—or JSON with padding是一种很阴险的技术,网站开发者围绕着浏览器限制(请求数据来自第三方范围)想出来的。
  • 使用脚本标签而不是通常的XMLHttpRequest加载外部内容可以绕过这些限制。给DOM加载添加脚本标签并且立即执行它的内容。这种安全限制不好。
  • 远程请求的内容是一个正常json被函数调用封装( P in JSONP),如下:
    callbackFn({ a: 1, b: 2, c: 3})
  • JSONP URLs 通常接受一个查询字符串参数以便调用者可以指定回调函数的名称。开发者必须在它的代码中定义一个和服务端响应的回调函数名称一样的函数,并且当脚本标签被添加到文档里,这个函数将会以那个josn数据作为第一个参数被调用。类似jQuery这样的库,它以在内部创建全局函数来处理这个JSONP调用来自动处理,整理之后以免污染全局的命名空间。
  • 这对flatMap是一个完美的场景。我们获取数据,并且利用Rx.Observable.from组建一个features数组的Observable,然后我们就吧这个Observable合并到主Observable中:
var quakes = Rx.Observable.create(function(observer) {window.eqfeed_callback = function(response) {❶ observer.onNext(response);❷ observer.onCompleted();};loadJSONP(QUAKE_URL);❸ }).flatMap(function transform(dataset) {❹ return Rx.Observable.from(dataset.response.features);});❺ quakes.subscribe(function(quake) {var coords = quake.geometry.coordinates;var size = quake.properties.mag * 10000;L.circle([coords[1], coords[0]], size).addTo(map);});
  • 我们不再是手动管理流了。这里没有循环或者是提取单个地震对象并且过滤其他项的这种情况。
    如下就是上面1~5标记的的含义:
    1:这次onNext函数仅仅发生一次,它产生全部的json响应。
    2:由于我们将会仅仅生产一次,所以在onNext之后我们要标记完成的信号。
    3:我们使用flatMap链式调用create的结果,所以flatMap会处理从Observable来的每一个结果(这里只有一个),同时transform函数把它作为参数,并且把这个函数的Observable结果合并到原始的Observable。
    4:这里我们使用包含所有地震信息的的features数组创建Observable。由于flatMap的作用,那些地震变量包含的将会变成实际的Observable。
    5:订阅不会发生任何改变;它还是像以前一样处理地震的信息流。
  • There Is Always a Method
  • 到目前为止我们使用了rx.all.js中的RxJS操作符,但是通常这是其他的基础RxJS库操作符的一个合集,下面我们就看下RxJS-DOM,RxJS-DOM是一个附加库,它包含一个处理JSONP请求jsonRequest的操作符。这样就会节省我们的一些代码,并且我们不需要再使用不安全的全局函数了:
var quakes = Rx.DOM.jsonpRequest({url: QUAKE_URL,jsonpCallback: 'eqfeed_callback'}).flatMap(function(result) {return Rx.Observable.from(result.response.features);}).map(function(quake) {return {lat: quake.geometry.coordinates[1],lng: quake.geometry.coordinates[0],size: quake.properties.mag * 10000};});quakes.subscribe(function(quake) {L.circle([quake.lat, quake.lng], quake.size).addTo(map);});
  • 记住,为了使上面的代码运行起来,你必须在html中引入rx.dom.js这个文件。注意到我们使用了map操作符把地震对象转为了仅仅包含经度、纬度和量级的简单地震信息对象。在subscribe操作符中越少功能就越好。
  • Making It Real Time
  • 我们交互版本的地震应用程序还没有实时的更新地震信息。为了实现这个,我们使用在上章见到过的interval操作符和那个非常有用的distinct(去重)操作符。让我们一起来看下代码和它的改变:
var quakes = Rx.Observable.interval(5000).flatMap(function() {return Rx.DOM.jsonpRequest({url: QUAKE_URL,jsonpCallback: 'eqfeed_callback'}).retry(3);}).flatMap(function(result) {return Rx.Observable.from(result.response.features);}).distinct(function(quake) { return quake.properties.code; });quakes.subscribe(function(quake) {var coords = quake.geometry.coordinates;var size = quake.properties.mag * 10000;L.circle([coords[1], coords[0]], size).addTo(map);});
  • 在上述的代码中,我们滥用interval每隔5秒去发新的请求和处理。interval创建一个每隔5秒发射自增数的Observable。我们并没有对这些数字做任何处理,反而我们使用flatMap去重新获取JsonpRequest的数据。请注意到我们在本章开始时,尝试通过retry重新获取数据的问题。
  • 最后一个操作符是distinct,仅仅发送以前没有发射过的元素。它需要返回一个检查属性相等的函数。这样我们就绝不会重复绘制已经绘制过的地震。在不到20行里,我们制作了一个程序,它通过外部的JSONP URL从它的内容中提取具体数据,并且过滤掉已经导入的地震信息。最后按照量级大小成比例的呈现在地图上,所有的书写独立、清晰、简洁且不依赖外部状态。很好!这展现了Observable是如何来使用的。
  • Ideas for Improvements
    这里是一些把你新学到的RxJS技能用到这个小应用程序中让它变得更有意思的建议:
  • 当我们的鼠标划过某个地震时,提供一个关于这个特定地震的详细信息的弹出。一个办法就是利用quakes中你想要展示的信息新建一个Observable,当鼠标悬停的时候动态的过滤。
  • 在每个页面的顶上设计一个展示到目前为止的地震数目的计时器,并且每天都可以重置。
  • 操作符的简介
  • 本章节已近给你展现了一些新的操作符,这里就是一他们的一些回顾,你可以在你程序的某些情景下使用。记住:你在 https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/observable.md这个github的链接上可以找到Rxjs操作符完整的API文档。
  • Rx.Observable.from
  • 默认行为:同步
  • 由于你程序中要使用的许多数据来自数组或者是迭代器,这种情况下,可以使用操作符创建一个关于他们的Observable。from就是其中你使用最多的一个操作符。
  • 使用from我们可以通过数组或者类似数组的对象(例如,参数对象,DOM节点列表)来创建,实现了iterable协议的类型如String,Map,Set也是可以的。
  • Rx.Observable.range
  • 列表内容
    默认行为:同步
  • range操作符产生一个发射指定范围整数的有限Observable。在很多场景下,它是多用涂的。例如,在扫雷游戏中,使用range可以在屏幕上显示初始的方格。
  • Rx.Observable.interval
    默认行为:异步
  • 每次你都需要准时的在间隔里生成值。interval操作符可能会被你当做生成器。由于interval按照每x毫秒(x为你传入的参数)来发射连续的整数,我们需要做的就是转化任何你需要的值。我们第后面一章的游戏就是严重依赖这个技术的。
  • Rx.Observable.distinct
  • 默认行为:与它过滤的的Observable一样
  • distinct是一个极简单的操作符节约了大朗的研发工作。它过滤序列已经发射的任何值。这阻止我们出错,它对发射的值使用字典代码模板,和对即将到来的值进行比较,你应该知道我说的是那种代码:
    这里写图片描述
  • distinct让我们使用一个特殊的对照函数。我们不传递参数的话,他会对数字或者字符串等使用严格的比较,并且在更复杂的对象情况下执行深度比较。
0 0
原创粉丝点击