[FE] 函数响应式流库探秘

xstream是专门为cycle.js定制开发的函数响应式流库(functional reactive stream library)。
它很简洁,只提供了Stream,Listener,Producer,MemoryStream四个概念。
我们先来学习xstream,然后再挖掘流(stream)与CPS的关系。

1. xstream的用法

(1)流(stream)

流可以看做一个事件流,流上面可以绑定多个监听器,
当流中某事件发生的时,会自动广播。

有了流之后,我们就可以对流的整体进行操作了。
在xstream中对流进行变换,是通过operator实现的,
operator处理一个或多个流,返回一个新的流。

let stream2=stream1.map(/*...*/);
let stream3=stream2.filter(/*...*/);

如上,mapfilter就是operator

(2)监听器(listener)

监听器用于处理当前发生的事件,时刻接受流中对所发生事件的广播。
在xstream中,监听器是一个包含nexterrorcomplete方法的对象,
流中每次事件发生,都会自动调用监听器的next方法,
流中有错误发生时,会调用error方法,
整个流停止,不再有事件发生时,调用complete方法。

let listener={
    next:val=>{/*...*/},
    error:err=>{/*...*/},
    complete:()=>{/*...*/}
};

(3)生产者(producer)

生产者用来生成流。
它是一个包含startstop方法的对象,用于表示流的开始和终止。
start函数中会使用listener,因此,listenernext方法实际上是在这里调用的。

import xs from 'xstream';

let producer={
    start(listener){
        // listener.next(/*...*/)
    },
    stop(){/*...*/}
};

let stream=xs.create(producer);

(4)有记忆的流(MemoryStream)

有记忆的流,和普通的流在operator方面和listener方面并无二致,
唯一不同的是,有记忆的流可以将当前事件中的值传给下一个事件。
(这里对主题帮助不大,我们暂且略过

2. 例子

我们学习了xstream的API,现在终于可以看到它的全貌了,

import xs from 'xstream';

let producer = {
    start: listener => {
        let i = 0;
        while (++i) {
            if (i > 10) {
                break;
            }

            listener.next(i);
        }
    },
    stop: () => { }
};

let stream1 = xs.create(producer);
let stream2 = stream1.map(x => x * 2);

stream2.addListener({
    next: val => console.log(val),
    error: val => { },
    complete: () => { }
});

最后结果会输出从2到20的偶数。

3. CPS

我们看到实际上是在流中调用了listener,即通过listener.next(i)广播了i
然后,流经历了一系列的变换,导致流广播的值发生了改变,
体现到最后的listener中,接收的值就不是最开始的i了,
而是i经历了x=> x*2之后的值i*2

(1)对流进行抽象

认识到问题的本质后,我们可以将流看成以下形式,

let stream = cont => {
    let i = 0;
    while (++i) {
        if (i > 10) {
            break;
        }

        cont(i);
    }
}

其中,cont表示continuation
(continuation的话题比较大,这里不影响阅读,暂略

(2)挂载listener

然后我们先不考虑对流进行变换,我们直接模拟挂载listener的场景,

stream(x => console.log(x));

好了,这个时候,实际上我们是将流的continuation传给了它,
结果自然是输出从1到10的数字了。

(3)对流进行变换

我们怎样对流进行变换呢,
实际上,我们需要做的就是将一个流变成另一个流,
或者说白了,就是改变cont,然后进行传递(CPS

这可能比较晦涩难懂,我们直接看例子吧,模拟一下x=>x*2
(这是可以运行的

let stream1 = cont => {
    let i = 0;
    while (++i) {
        if (i > 10) {
            break;
        }

        cont(i);
    }
};

let stream2 = cont => {
    let newCont = v => cont(v * 2);
    stream1(newCont);
};
// 简写为
// let stream2 = cont => stream1(v => cont(v * 2));

stream2(x=>console.log(x));

(4)实现mapfiltermerge

我们来尝试实现xstream中几个常用的operator,它们都返回一个新的流。

//map是对流中的每个值进行变换
let map = function (fn) {
    let stream = this;
    return cont => stream(x => cont(fn(x)));
};
let stream2 = map.call(stream1, x => x * 2);

//filter是对流中的值进行过滤
let filter = function (fn) {
    let stream = this;
    return cont => stream(x => fn(x) && cont(x));
};
let stream3 = filter.call(stream1, x => x % 2 != 0);

//merge是合并两个流
let merge = function (otherStream) {
    let stream = this;
    return cont => {
        stream(cont);
        otherStream(cont);
    };
};
let stream4 = merge.call(stream2, stream3);

4. 总结与展望

xstream采用了流的概念,实现了事件源与事件处理逻辑的分离,
而且,对流的变换都是一些纯函数,组合起来更方便,
因此成就了cycle.js这个优美的框架,从而MVI全新的架构模式破土而出,
这一切,一定会在人机交互界面的解决方案上开启新的篇章啊。

5. 参考

xstream
Cycle.js Document

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,562评论 18 139
  • https://nodejs.org/api/documentation.html 工具模块 Assert 测试 ...
    KeKeMars阅读 6,293评论 0 6
  • Java基础常见英语词汇(共70个)['ɔbdʒekt] ['ɔ:rientid]导向的 ...
    今夜子辰阅读 3,263评论 1 34
  • 随着前端框架react,angular以及vue的流行,响应式编程也开始在前端领域得以广泛应用。因此,了解并且理解...
    SCQ000阅读 7,448评论 1 53
  • 煌煌达摩,为爱沉沦 ——《嫌疑人X的献身》读后感 《嫌疑人X的献身》这本书是东野圭吾的代表作,...
    棉花五阅读 237评论 0 1