【问题标题】:Assemble paginated ajax data in a Bacon FRP stream在培根 FRP 流中组装分页 ajax 数据
【发布时间】:2023-03-26 08:00:01
【问题描述】:

我正在使用 Bacon.js 学习 FRP,并希望将来自分页 API 的数据组装到流中。

使用数据的模块有这样的消费API:

// UI module, displays unicorns as they arrive
beautifulUnicorns.property.onValue(function(allUnicorns){
  console.log("Got "+ allUnicorns.length +" Unicorns");
  // ... some real display work
});

每次获取新数据集时,组装数据的模块都会从 API 和 pushes 请求顺序页面到流上:

// beautifulUnicorns module
var curPage = 1
var stream = new Bacon.Bus()
var property = stream.toProperty()
var property.onValue(function(){}) # You have to add an empty subscriber, otherwise future onValues will not receive the initial value. https://github.com/baconjs/bacon.js/wiki/FAQ#why-isnt-my-property-updated

var allUnicorns = [] // !!! stateful list of all unicorns ever received. Is this idiomatic for FRP?

var getNextPage = function(){
  /* get data for subsequent pages.
     Skipping for clarity */
}

var gotNextPage = function (resp) {
  Array.prototype.push.apply(allUnicorns, resp) // just adds the responses to the existing array reference
  stream.push(allUnicorns)
  curPage++
  if (curPage <= pageLimit) { getNextPage() }
}

我如何订阅流,以便为我提供曾经收到的所有独角兽的完整列表?这是flatMap 还是类似的?我不认为我需要一个新的流出来,但我不知道。对不起,我是 FRP 思维方式的新手。需要明确的是,组装数组是可行的,感觉就像我没有做惯用的事情。

我没有为此使用 jQuery 或其他 ajax 库,所以我没有使用 Bacon.fromPromise

您可能还想知道为什么我的消费模块想要整个集合而不是增量更新。如果它只是附加行可能没问题,但在我的情况下,它是一个无限滚动,它应该在以下情况下绘制数据:1. 数据可用 2. 区域在屏幕上。

【问题讨论】:

    标签: javascript frp bacon.js


    【解决方案1】:

    这可以通过.scan() 方法完成。此外,您还需要一个发出一页项目的流,您可以使用.repeat() 创建它。

    这是一个草稿代码(抱歉没有测试):

    var itemsPerPage = Bacon.repeat(function(index) {
      var pageNumber = index + 1;
      if (pageNumber < PAGE_LIMIT) {
        return Bacon.fromCallback(function(callback) {
          // your method that talks to the server 
          getDataForAPage(pageNumber, callback); 
        });
      } else {
        return false;
      }
    });
    
    var allItems = itemsPerPage.scan([], function(allItems, itemsFromAPage) {
      return allItems.concat(itemsFromAPage);
    });
    
    
    // Here you go
    allItems.onValue(function(allUnicorns){
      console.log("Got "+ allUnicorns.length +" Unicorns");
      // ... some real display work
    });
    

    如您所见,您也不需要.onValue(function(){}) hack 和curPage 外部状态。

    【讨论】:

      【解决方案2】:

      这是使用flatMapfold 的解决方案。在处理网络时,您必须记住数据返回的顺序可能与您发送请求的顺序不同——这就是折叠和映射组合的原因。

      var pages = Bacon.fromArray([1,2,3,4,5])
      var requests = pages.flatMap(function(page) {
        return doAjax(page)
        .map(function(value) {
          return {
            page: page,
            value: value
          }
        })
      }).log("Data received")
      
      var allData = requests.fold([], function(arr, data) {
        return arr.concat([data])
      }).map(function(arr) {
        // I would normally write this as a oneliner
        var sorted = _.sortBy(arr, "page")
        var onlyValues = _.pluck(sorted, "value")
        var inOneArray = _.flatten(onlyValues)
        return inOneArray
      })
      
      allData.log("All data")
      
      function doAjax(page) {
        // This would actually be Bacon.fromPromise($.ajax...)
      
        // Math random to simulate the fact that requests can return out
        // of order
        return Bacon.later(Math.random() * 3000, [ 
          "Page"+page+"Item1", 
          "Page"+page+"Item2"])
      }
      

      http://jsbin.com/damevu/4/edit

      【讨论】:

      • 如果您需要数据按顺序到达。您可以将flatMap 替换为flatMapWithConcurrencyLimit