FUNCTIONAL REACTIVE PROGRAMMING

原文地址: http://blog.kissyui.com/2014/08/07/functional-reactive-programming/

function reactive programming (frp) 是一种函数式语言的编程范式,通过响应式的编程可以构建出事件流,事件流可以再通过函数式的转换/过滤/组合,最终形成可用的结果。同 promise 类似可以有效得消除异步调用场景下的嵌套回调。和 promise 侧重于一次性的成功/失败不同,frp 则侧重于离散的源源不断的事件流。

frp 根据概念和实现的差别并没有一致的约定,这里介绍 kissy frp 的 api 及其用法,详细的概念介绍参见:The introduction to Reactive Programming you’ve been missing.

api

EventStream 事件流类

Property 属性类,继承于事件流类

构建方法

createEventStream: 构建一个事件流 (EventStream),然后可以往事件流中添加值,例如

reactive.createEventStream(function(fire){
  fire('something')
});

createProperty: 构建一个属性 (Property),属性和事件的区别在于后面 onValue 的处理不同.

实例方法

EventStream.prototype.onValue 监听事件流所添加的值

Property.prototype.onValue 监听属性的最近一个值,和 EventStream 的区别点在于如果先在事件流中添加值然后再监听,事件流中的值会监听不到(已经触发)。而 property 会缓存最近的值,在 onValue 会取出来缓存的值。

EventStream.prototype.startsWith 设置事件流的初始值, 在 onValue 时立即取出来使用

函数式方法

EventStream.prototype.combine 将多个流组合为一个流,这个流的事件值为多个流事件值的组合,例如

stream1 = reactive.createEventStream(function(fire){
  fire(1)
});
stream2 = reactive.createEventStream(function(fire){
  fire(2)
});
stream3 = stream1.combine(stream2);
stream3.onValue(function(v){
  v // => [1,2]
});

EventStream.prototype.filter 返回一个新的事件流,该事件流只有在当前事件流的值通过过滤时才添加值,例如

stream2 = stream1.filter(function(v){
    return v>1;
});

EventStream.prototype.map 返回一个新的事件流,该事件流是对当前事件流转换的结果,结果可以又是一个事件流,即形成一种委托的关系。

例如简单的转换

stream2 = stream1.map(function(v){
  return 2*v;
});

或者嵌套的转换,新的事件流的值又是由另一个事件流产生,常用于异步的场景

stream2 = stream1.map(function(v){
  return reactive.createEventStream(function(fire){
       setTimeout(function(){
          fire(v+1);
       },100);
  });
});

example

api 很少,用起来也很简单,这里举两个简单的例子说明下:

自动补全

demo, 自动补全是一个 frp 合适而且简单的应用场景, 初始事件流就是用户对于单个 input 的输入,该事件流经过简单的过滤(非空),转换为对于服务器请求的事件流,最终服务器返回后的值作为结果渲染。示例代码如下:

// 初始事件流
reactive.createEventStream(function(fire){
   input[0].oninput = function () {
     fire(this.value);
   };
})
// 过滤
.filter(function(v){
  return !!v.trim();
})
// 去空格
.map(function(v){
  return v.trim();
})
// 转化为服务器请求流
.map(function(v){
  return reactive.createEventStream(function(fire){
    io.jsonp('xx.json',{q:v},function(data){
        fire(data)
    });
  });
})
// 获取最终值
.onValue(function(data){
   // render data
});

大家可以想下以前用回调是如果实现的,而 frp 则有效得使用组合替代了嵌套回调。

计算器

demo ,计算器则演示了多个事件流的组合性,用户在每个输入框的输入都形成了一个事件流,而多个事件流组合的结果经过计算形成了最终的计算结果。示例代码如下

// 事件流1
reactive.createEventStream(function(fire){
   input1.oninput = function () {
     fire(this.value);
   };
})
// 组合事件流2
.combine(reactive.createEventStream(function(fire){
   input2.oninput = function () {
     fire(this.value);
   };
}))
// 转换结果
.map(function(v){
  return v[0]+v[1];
})
// 渲染最终值
.onValue(function(v){
});

现在就使用

在 kissy 中可以通过配置 gallery 包 kg 为 http://g.tbcdn.cn/kg/ 然后 use(‘kg/reactive/0.2.0/’) 即可。

gallery 组件现在也可以使用 bower 安装到本地(公司内部),配置 .bowerrc 为

{
    "shorthand_resolver":"http://gitlab.alibaba-inc.com/{{{owner}}}/{{{package}}}.git"
}

添加 bower.json

{
    "name": "test-project",
    "private": true,
    "dependencies": {
        "reactive": "kg/reactive"
    }
}

在根目录执行 bower install 即可安装组件到本地 bower_components 目录,目录可以通过在 .bowerrc 中配置 directory 改变,详见: bower 文档

Advertisements

发表评论

Fill in your details below or click an icon to log in:

WordPress.com 徽标

You are commenting using your WordPress.com account. Log Out /  更改 )

Google photo

You are commenting using your Google account. Log Out /  更改 )

Twitter picture

You are commenting using your Twitter account. Log Out /  更改 )

Facebook photo

You are commenting using your Facebook account. Log Out /  更改 )

Connecting to %s