如何使用 Bluebird Promises 进行分页?

How can I do pagination with Bluebird Promises?

我有类似的东西

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 是我用来处理每笔交易的函数。

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

您应该能够使用 promisify()/promisifyAll()trader.getTrades() 转换为 returns 承诺的异步版本。然后,像这样的东西应该可以正常工作:

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 页交易。

首先,让我们隐藏那个丑陋的回调 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 不是超级快,因为它会在每次请求后创建一个新数组,但这是最优雅的。

此函数将 return 一个承诺,当所有请求都发出时,该承诺会解析出大量的所有结果。这当然可能不是你想要的——也许你想立即显示第一个结果,然后延迟加载更多?那么单个承诺不是您想要的工具,因为这种行为更像流。然而,它仍然可以用 promises 来写:

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

这是我自己的通过 promise 进行分页的解决方案:方法 page, as part of the 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 }