javascript – 如何连接到源?

我有以下代码:

const Cycle = require('@cycle/core');
const {Observable} = require('rx');

function main(sources) {
    const B$= sources.driverA
        .concat(Observable.just('b'))
        .concat(Observable.just('c'));

    const C$= sources.driverB.map(x => x.toUpperCase());

    return {
        driverA: Observable.just('a'),
        driverB: B$,
        driverC: C$
    }
}

Cycle.run(main, {
    driverA: (A$) => A$,
    driverB: (B$) => B$,
    driverC: msg$=> { msg$.subscribe(msg => console.log(msg)) }
});

我希望控制台上有三行:A,B和C,但我只得到最后一行.看来,即使B $.tap(console.log)输出全部三个(“a”,“b”,“c”),driverC也只获取最后一条消息;

这种行为的解释是什么?如何将所有三条消息传播到driverC?

版本:

> @cycle / core @ 6.0.3
> rx@4.1.0

最佳答案 行为的解释

实际上,这并不容易解释.这是由于cycle.run如何连接它的循环.以下代码在trycicle中运行:

const Cycle = require('@cycle/core');
const {Observable} = require('rx');

function main(sources) {
    const B$= sources.driverA
        .concat(Observable.just('b'))
        .concat(Observable.just('c'))
        .concat(Observable.just('d'));

    const C$= sources.driverB.map(x => x.toUpperCase());

    return {
        driverA: Observable.just('a'),
        driverB: B$,
        driverC: C$
    }
}

Cycle.run(main, {
    driverA: (A$) => A$.tap(msg => console.log(msg)),
    driverB: (B$) => B$.tap(msg => console.log(msg)),
    driverC: msg$=> { msg$.subscribe(msg => console.log(msg)) }
});

并且只显示d D.所以它实际上是显示的最后一个字母.

现在如果你运行这个:

const Cycle = require('@cycle/core');
const {Observable} = require('rx');

function main(sources) {
    const B$= sources.driverA
        .concat(Observable.just('b').delay(1))
        .concat(Observable.just('c'))
        .concat(Observable.just('d'));

    const C$= sources.driverB.map(x => x.toUpperCase());

    return {
        driverA: Observable.just('a'),
        driverB: B$,
        driverC: C$
    }
}

Cycle.run(main, {
    driverA: (A$) => A$.tap(msg => console.log(msg)),
    driverB: (B$) => B$.tap(msg => console.log(msg)),
    driverC: msg$=> { msg$.subscribe(msg => console.log(msg)) }
});

你得到一个A b B c C d D这是你所期望的.

发生的事情是,通过主题将驱动程序连接到源,并按顺序执行.哪个订单? obj中var x中属性的枚举顺序(未指定且因此不能依赖)可能与浏览器有关(参见Does ES6 introduce a well-defined order of enumeration for object properties?)).现在chrome和firefox最新版本似乎按照字母数字属性的定义顺序枚举属性,但是数值属性的数字顺序(following ES2015 spec).

所以这里,driverA首先连接到源,它启动相应的数据流.当driverB连接到源,同样的事情.由于您编写B $的方式,该数据流是同步的.因此,当订阅即进行布线时,所有数据a b c d从B $同步流动,并且当driverC接线时,B $已经完成.鉴于使用replaySubject(1)进行布线,该布线将在完成之前为您提供最后一个发射值,即d.

因此,由于同步性,顺序很重要:如果B和C首先接线,那就没问题了.运气好的话,执行顺序不够.

为了说服您,我按照拓扑顺序对您的流进行排序的代码按预期工作:

const Cycle = require('@cycle/core');
const {Observable} = require('rx');

function main(sources) {
    const B$= sources.driverA
        .concat(Observable.just('b'))
        .concat(Observable.just('c'))
        .concat(Observable.just('d'));

    const C$= sources.driverB.map(x => x.toUpperCase());

    return {
        driverC: C$,
        driverB: B$,
        driverA: Observable.just('a'),
    }
}

Cycle.run(main, {
    driverA: (A$) => A$.tap(msg => console.log(msg)),
    driverB: (B$) => B$.tap(msg => console.log(msg)),
    driverC: msg$=> { msg$.subscribe(msg => console.log(msg)) }})

你如何传播所有三个消息

好吧,要么按照拓扑顺序订购接收器,要么删除同步性.我添加了延迟(1)以使数据流在下一个滴答时继续,此时driverC已经连线以接收下一个值.这可能是最强大的选项,因为拓扑顺序在这里计算可能并不总是显而易见,可能会随着源的隔行扫描而改变,并依赖于依赖于浏览器的对象属性枚举(!).

另外,当无法避免数据流的同步时,通常首先使用发布连接所有源来处理连接问题,然后连接以便在数据流动时,所有源都已准备好接收它.

点赞