B.2.2 响应序列
有了这个非常简要的 Observable(以及 F/RP)的概述给予我们灵感和激励,现在我要展示“响应式 Observable”的一个小子集的修改版,我称之为“响应式序列”。
首先,让我们从如何使用名为 react(..) 的 asynquence 插件工具创建一个 Observable 开始:
var observable = ASQ.react( function setup(next){ listener.on( "foobar", next ); } );
现在,来看看如何定义一个能“响应”这个 observable 的序列(在 F/RP 中,这通常称为“订阅”):
observable .seq( .. ) .then( .. ) .val( .. );
所以,只要结束 Observable 链接就定义了序列。很简单,是不是?
在 F/RP 中,事件流通常从一系列函数变换中穿过,比如 scan(..) 、map(..) 、reduce(..) ,等等。使用响应式序列的话,每个事件从一个序列的新实例中穿过。我们来看一个较具体的例子:
ASQ.react( function setup(next){ document.getElementById( "mybtn" ) .addEventListener( "click", next, false ); } ) .seq( function(evt){ var btnID = evt.target.id; return request( "http://some.url.1/?id=" + btnID ); } ) .val( function(text){ console.log( text ); } );
这个响应序列的“响应”部分来自于分配了一个或多个事件处理函数来调用事件触发器(调用 next(..) )。
响应序列的“序列”部分就和我们已经研究过的序列完全一样:每个步骤可以使用任意合理的异步技术,从 continuation 到 Promise 再到生成器。
一旦建立起响应序列,只要事件持续触发,它就会持续启动序列实例。如果想要停止响应序列,可以调用 stop() 。
如果响应序列调用了 stop() ,停止了,那你很可能希望事件处理函数也被注销。可以注册一个 teardown 处理函数来实现这个目的:
var sq = ASQ.react( function setup(next,registerTeardown){ var btn = document.getElementById( "mybtn" ); btn.addEventListener( "click", next, false ); // 一旦sq.stop()被调用就会调用 registerTeardown( function(){ btn.removeEventListener( "click", next, false ); } ); } ) .seq( .. ) .then( .. ) .val( .. ); // 将来 sq.stop();
处理函数 setup(..) 中的 this 绑定引用和响应序列 sq 一样,所以你可以使用这个 this 引用向响应序列定义添加内容,调用像 stop() 这样的方法,等等。
这里是一个来自 Node.js 世界的例子,使用了响应序列来处理到来的 HTTP 请求:
var server = http.createServer(); server.listen(8000); // 响应式observer var request = ASQ.react( function setup(next,registerTeardown){ server.addListener( "request", next ); server.addListener( "close", this.stop ); registerTeardown( function(){ server.removeListener( "request", next ); server.removeListener( "close", request.stop ); } ); }); // 响应请求 request .seq( pullFromDatabase ) .val( function(data,res){ res.end( data ); } ); // 节点清除 process.on( "SIGINT", request.stop );
使用 onStream(..) 和 unStream(..) ,触发器 next(..) 也很容易适配节点流:
ASQ.react( function setup(next){ var fstream = fs.createReadStream( "/some/file" ); // 把流的"data"事件传给next(..) next.onStream( fstream ); // 侦听流结尾 fstream.on( "end", function(){ next.unStream( fstream ); } ); } ) .seq( .. ) .then( .. ) .val( .. );
也可以通过序列合并来组合多个响应序列流:
var sq1 = ASQ.react( .. ).seq( .. ).then( .. ); var sq2 = ASQ.react( .. ).seq( .. ).then( .. ); var sq3 = ASQ.react(..) .gate( sq1, sq2 ) .then( .. );
主要的一点是:ASQ.react(..) 是 F/RP 概念的一个轻量级的修改版,也是术语“响应序列”的意义所在。响应序列通常能够胜任基本的响应式用途。
这里有一个使用 ASQ.react(..) 管理 UI 状态的例子(http://jsbin,com/rozipaki/6/edit?js,output ),还有一个通过 ASQ.react(..) 处理 HTTP 请求 / 响应流的例子(https://gist.github.com/getify/bba5ec0de9d6047b720e )。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论