4

我有以下代码:

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?

版本:

  • @cycle/core@6.0.3
  • rx@4.1.0
4

2 回答 2

1

行为说明

嗯,实际上这并不容易解释。这是由于如何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)) }
});

并且只显示a 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 a A b B c C d D了你所期望的。

发生的事情是run通过主题将驱动程序连接到源,并按顺序执行。哪个顺序?中的属性枚举顺序,var x in obj未指定,因此不能依赖 - 可能取决于浏览器(参见ES6 是否为对象属性引入了明确定义的枚举顺序?))。现在chromefirefox最新版本似乎按字母数字属性的定义顺序枚举属性,但数字属性的数字顺序(遵循 ES2015 规范)。

所以在这里,driverA首先连接到源,它启动相应的数据流。当driverB连接到源时,同样的事情。由于您编写的方式,该数据流是同步B$的。所以当subscribeie连线的时候,所有的数据都是a b c d同步的,连线B$的时候就已经完成了。鉴于接线是用 a 制成的,该接线会为您提供完成前最后发出的值,即.driverCB$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)) }})

您如何传播所有三个消息

好吧,要么按拓扑顺序排列您的接收器,要么删除同步性。我添加了一个delay(1)让数据流在下一个滴答声中继续,此时driverC已经连线以接收下一个值。这可能是最强大的选项,因为拓扑顺序可能并不总是像这里那样明显计算,可能会随着源的交错而变化,并且依赖于浏览器相关的对象属性枚举(!)。

另外需要注意的是,当数据流的同步性无法避免时,您通常通过使用publish先连接所有源来处理连接问题,然后connect当数据流动时,所有源都已准备好接收它。

于 2016-06-03T20:29:07.280 回答
1

只需添加一个delayafter driverA

const B$ = sources.driverA.delay(1)

WebpackBin 示例。

或者,您可以使用列出的所有 observables 唤起concat一次并延迟它。

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

WebpackBin 示例 #2。

需要记住的是,该main功能只是连接管道。run打开水。

于 2016-06-03T20:59:49.063 回答