【问题标题】:How can I do pagination with Bluebird Promises?如何使用 Bluebird Promises 进行分页?
【发布时间】:2015-04-17 10:05:44
【问题描述】:

我有类似的东西

new Promise (resolve, reject) ->
  trader.getTrades limit, skip, (err, trades) ->
    return reject err if err

    resolve trades
.each (trade) ->
  doStuff trade

limit 设置为任意数字,例如10skip0 开始。我想继续增加skip,直到没有更多trades

doStuff 是我用来处理每笔交易的函数。

这是第一次,但我想以分页方式获得更多交易。具体来说,我想以更高的skip 运行trader.getTrades,直到trades.length 为0

【问题讨论】:

  • 向我们展示trader.getTrades 的文档以及limitskip 的值。
  • 您实际上是在以某种方式使用承诺,还是 .each doStuff 对您的所有代码产生副作用?
  • trader.getTrades 不是一个真正的函数。将其视为从数据存储返回交易的一种方式。
  • 另外,你知道从第一个响应中的总项目数是多少吗?或者你想用更高的skip 值调用直到trades.length===0??
  • 好的。那么“分页时尚”对你来说究竟意味着什么?如果你告诉我们你想要什么,我可以告诉你怎么做,否则你的问题的答案是“是”:-)

标签: javascript node.js promise bluebird


【解决方案1】:

这是我自己的通过 Promise 进行分页的解决方案:方法 page,作为 spex 库的一部分。

它还可以让您根据需要限制处理并提供负载平衡。

示例

var promise = require('bluebird');
var spex = require('spex')(promise);

function source(index, data, delay) {
    // create and return an array/page of mixed values
    // dynamically, based on the index of the sequence;
    switch (index) {
        case 0:
            return [0, 1, promise.resolve(2)];
        case 1:
            return [3, 4, promise.resolve(5)];
        case 2:
            return [6, 7, promise.resolve(8)];
    }
    // returning nothing/undefined indicates the end of the sequence;
    // throwing an error will result in a reject;
}

function dest(idx, data, delay) {
    // data - resolved array data;
    console.log("LOG:", idx, data, delay);
}

spex.page(source, dest)
    .then(function (data) {
        console.log("DATA:", data); // print result;
    });

输出

LOG: 0 [ 0, 1, 2 ] undefined
LOG: 1 [ 3, 4, 5 ] 3
LOG: 2 [ 6, 7, 8 ] 0
DATA: { pages: 3, total: 9, duration: 7 }

【讨论】:

    【解决方案2】:

    首先,让我们隐藏那个丑陋的回调 api:

    var getTrades = Promise.promisify(trader.getTrades, trader);
    

    现在,为了遍历分页 api,我们将使用简单的递归下降:

    function getAllTrades(limit, arr) {
        if (!arr) arr=[];
        return getTrades(limit, arr.length).then(function(results) {
             if (!results.length)
                 return arr;
             else
                 return getAllTrades(limit, arr.concat(results));
        });
    }
    

    诚然,concat 并不是超级快,因为它会在每次请求后创建一个新数组,但这是最优雅的。

    这个函数会返回一个promise,当所有请求都完成时,这个promise会解析出一个巨大的数组。这当然可能不是您想要的 - 也许您想立即显示第一个结果,并且只是懒惰地加载更多?那么单个 Promise 就不是您想要的工具,因为这种行为更像流。然而,它仍然可以用 Promise 来编写:

    getTradeChunks = (limit, start = 0) ->
      getTrades limit, start
      .then (chunk) ->
        throw new Error("end of stream") if not chunk.length
        s = start+chunk.length
        [chunk, -> getTradeChunks limit, s]
    
    rec = ([chunk, cont]) ->
      Promise.each chunk, doStuff
      .then -> waitForClick $ "#more"
      .then cont
      .then rec, err
    end = (err) ->
      $ "#more"
      .text "no more trades"
    getTradeChunks 15
    .then rec, err
    

    【讨论】:

    • 最干净的?现在我想知道 - 如果只有一种聚合方法:) 我认为这可能是一个支持泛型迭代的用例。
    • @BenjaminGruenbaum:你是说collection methods吗?我看不出它们在这里如何使用。
    • 哇,很酷,这也是纯 JS 中的内容吗?我现在使用ps.map 做类似的事情,但想要没有流概念的选项。
    • @aarosil:不,它是咖啡脚本,就像 OP 的原始代码一样
    • @Shamoon 当然你可以省略数组,只传递它的长度。但是如果结果破坏了你的机器,你为什么要遍历整个事情呢?这就是为什么我在原始 cmets 中询问您的用例。
    【解决方案3】:

    您应该能够使用promisify()/promisifyAll()trader.getTrades() 转换为返回承诺的异步版本。然后,这样的事情应该会很好地工作:

    function getAllTrades(limit, offset, query) {
    
        var allTrades = [];
    
        function getTrades(limit, offset, query){
            return trader.getTradesAsync(limit, offset, query)
                .each(function(trade) {
                    allTrades.push(trade)
                    // or, doStuff(trade), etc.
                })
                .then(function(trades) {
                    if (trades.length === limit) {
                        offset += limit;
                        return getTrades(limit, offset, query);
                    } else {
                        return allTrades;
                    }
                })
                .catch(function(e) {
                    console.log(e.stack);
                })
        }
    
        return getTrades(limit, offset, query)
    }
    

    如果您事先知道总交易次数,您可以使用 .map{concurrency: N} 的不同策略一次获得 N 页交易。

    【讨论】:

    • 我喜欢 allTrades 和闭包的巧妙技巧,但您可以通过将旧结果连接到新结果来更优雅地解决这个问题。
    • 嗯,没错,但是随着 HFT 的普及,我认为这些将是巨大的阵列,而且最好安全地发挥作用;)
    猜你喜欢
    • 2016-09-24
    • 1970-01-01
    • 2016-05-18
    • 2017-03-04
    • 2015-11-14
    • 2017-04-03
    • 2017-04-04
    • 1970-01-01
    • 2015-06-15
    相关资源
    最近更新 更多