【问题标题】:Complex operation with BaconJS (FRP)BaconJS (FRP) 的复杂操作
【发布时间】:2015-06-26 05:36:21
【问题描述】:

我正在尝试在 BaconJs 中做这个相对复杂的操作。

基本上,这个想法是不断尝试每个check,直到您获得“通过”状态或者它们都失败了。问题是“待定”状态有一个可解决检查的 Observables 列表(由 jquery ajax 请求构建)。出于性能原因,您需要按顺序尝试每个 Observable,直到它们全部通过或一个失败。

这是完整的伪算法:

  • 检查每项检查。 check 包含 idstatus = 失败/通过/待定。如果挂起,它包含observables 的列表。
    • 如果 status = pass,则返回 id(大功告成!)
    • 如果状态 = 失败,则尝试下一次检查
    • 如果状态 = 待定
      • 按顺序尝试每个 observable
        • 如果可观察到的结果为“假”,则尝试下一次检查
      • 如果到达可观察列表的末尾并且结果为“真”,则返回 id(大功告成!)

这是培根代码。当 Observables 是 Ajax 请求时,它不起作用。 基本上,发生的事情是它跳过了待处理的检查......它不等待 ajax 调用返回。如果我将 log() 放在 filter() 之前,它不会记录挂起的请求:

    Bacon.fromArray(checks)
      .flatMap(function(check) {

        return check.status === 'pass' ? check.id :
          check.status === 'fail' ? null :
            Bacon.fromArray(check.observables)
              .flatMap(function(obs) { return obs; })
              .takeWhile(function(obsResult) { return obsResult; })
              .last()
              .map(function(obsResult) { return obsResult ? check.id : null; });
      })
      .filter(function(contextId) { return contextId !== null; })
      .first();

更新:当检查如下所示时代码有效:[失败,失败,待定]。但是当检查看起来像这样时它不起作用:[失败,挂起,通过]

【问题讨论】:

  • 所以检查应该彼此并行评估?
  • 没有按顺序检查。在清除当前检查之前,您无法尝试下一次检查
  • 听起来你实际上是在寻找承诺,那么。虽然 iirc Bacon 的 fire-once 属性和 flatMap 具有相同的语义。
  • 对于此类问题,使用 jsfiddle 或 jsbin 的工作示例将非常有用。

标签: functional-programming frp rxjs bacon.js


【解决方案1】:

我对 RxJS 比对 Bacon 更熟悉,但我想说您没有看到所需行为的原因是因为 flatMap 不等待任何人。

它快速连续通过[fail, pending, pass]fail 返回 null 并被过滤掉。 pending 启动一个 observable,然后接收 pass 立即返回 check.id (培根可能不同,但在 RxJS flatMap 中不接受单个值返回)。 check.id 通过filter 并点击first,此时它完成并且它只是取消对ajax 请求的订阅。

一个快速的解决方法可能是使用concatMap 而不是flatMap

在 RxJS 中,尽管我会将其重构为(免责声明未经测试):

Rx.Observable.fromArray(checks)
  //Process each check in order
  .concatMap(function(check) {
     var sources = {
       //If we pass then we are done
       'pass' : Rx.Observable.just({id : check.id, done : true}),
       //If we fail keep trying
       'fail' : Rx.Observable.just({done : false}),

       'pending' : Rx.Observable.defer(function(){ return check.observables;})
                                .concatAll()
                                .every()
                                .map(function(x) { 
                                  return x ? {done : true, id : check.id} : 
                                             {done : false};
                                })
     };

     return Rx.Observable.case(function() { return check.status; }, sources);
  })
  //Take the first value that is done
  .first(function(x) { return x.done; })
  .pluck('id');

上面做的是:

  1. 连接所有检查
  2. 使用case 运算符而不是嵌套三元组来传播。
  3. 失败通过
  4. 如果 pendingcheck.observables 创建一个扁平化的 observable,如果它们都是真的,那么我们就完成了,否则继续下一个
  5. 使用谓词值first 来获取返回的第一个值完成
  6. [可选地]去除我们关心的价值。

【讨论】:

  • 不幸的是,虽然这可能有效,但我需要串行执行每个 check.observable,所以 concatAll 不是我需要的(在培根中,它是 combineAsArray)
  • 不确定我是否理解。 concatAll 确实连续工作。在前一个完成之前,它不会订阅下一个 Observable。
  • 不幸的是,除非我弄错了,否则培根似乎没有 #concatAll 等价物。 (我可能是错的,但我没有看到)。不过谢谢。你的回答把我推向了正确的方向
【解决方案2】:

我同意基于@paulpdaniels Rx 的回答。问题似乎是当使用flatMap 时,Bacon.js 不会等待您的第一个“检查流”完成后再启动一个新的。只需将flatMap 替换为flatMapConcat

【讨论】:

  • 成功了。在下面发布完整答案以供后代使用
【解决方案3】:

感谢@raimohanska 和@paulpdaniels。答案是使用#flatMapConcat。这将基本上是并行完成的异步调用列表转换为按顺序完成的调用序列(并注意最后一个“检查”被编程为始终通过,因此它总是输出一些东西):

   Bacon.fromArray(checks)
      .flatMapConcat(function(check) {

        var result = check();

        switch(result.status) {
          case 'pass' :
          case 'fail' :
            return result;
          case 'pending' :
            return Bacon.fromArray(result.observables)
              .flatMapConcat(function(obs) { return obs; })
              .takeWhile(function(obsResult) { return obsResult.result; })
              .last()
              .map(function (obsResult) { return obsResult ? {id: result.id, status: 'pass'} : {status: 'fail'}; });

        }
      })
      .filter(function(result) { return result.status === 'pass'; })
      .first()
      .map('.id');

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-13
    • 2016-01-04
    • 2016-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多