如何用下划线对一系列承诺进行分组?

How to group by a range of promises with underscore?

我可以成功地从 redis 收集指标,但我还想通过设备 ID 收集它。最简单的方法是什么?

在这里,我使用下划线和蓝鸟。

想要的对象:

[
  { 
    deviceId    : 'foo',

    stats: [
      {
        date        : '20160215',
        temperature : 32.3,
        uptime      : 10.0,
        frequency   : 0.2
      },
      {
        date        : '20160216',
        temperature : 12.3,
        uptime      : 15.0,
        frequency   : 0.3
      }
      // ,...
    ]
  },

  { 
    deviceId    : 'bar',

    stats: [
      {
        date        : '20160215',
        temperature : 42.3,
        uptime      : 0.3,
        frequency   : 0.5
      },
      {
        date        : '20160216',
        temperature : 32.3,
        uptime      : 1.2,
        frequency   : 0.5
      }
      // ,...
    ]
  }
]

数据存储在redis中的有序集合:

// metric:device:device-id:daily

temperature:device:standart-faf4-4279-a040-0e729e53f5c1:daily
uptime:device:standart-faf4-4279-a040-0e729e53f5c1:daily
frequency:device:standart-faf4-4279-a040-0e729e53f5c1:daily

我的代码:

  var metrics = [ 'temperature', 'uptime', 'frequency' ];

  var signals = _
    .chain(statsOfToday)
    .pluck('deviceId')
    .map(function(deviceId) {
      return _.map(metrics, function(metric) {
        return redis.zrangebyscoreAsync(
          metric +":device:"+ deviceId +":daily",
          20160215, 20160216
        );
      })
    })
    .flatten()
    .value();

  // in here deviceId is lost
  return Promise.all(signals)
    .then(function(results) {
      return _
        .chain(results)
        .flatten()
        .map(function(x) {
          // data in redis stored as: metric-timestamp
          return x.split("-")[0];
        })
        .value();
    });

我知道您的第一条语句会生成一组不包含它们所引用的 deviceId 的值的承诺,这使得在第二步中处理它们变得更加困难。

那么这个怎么样:

var signals = _
  .chain(statsOfToday)
  .pluck('deviceId')
  .map(function(deviceId) {
    return _.map(metrics, function(metric) {
      return redis.zrangebyscoreAsync(
        metric +":device:"+ deviceId +":daily", 20160215, 20160216
      ).then(function (data) {
        return data.map(function (item) {
            return {
              deviceId: deviceId,
              metric: metric,
              data: item.split("-")[0]
            };
        });
      });
    })
  })
  .flatten()
  .value();

通过这种方式,您可以获得包含 deviceIdmetric 和来自 Redis 的关联 data 的值的承诺数组。

Promise.all(signals) 之后可以将其分组 data/massaging 成形状。


更易读且更易于重用:

function getDeviceMetricAsync(deviceId, metric, min, max) {
  var key = metric + ":device:" + deviceId + ":daily";
  return redis.zrangebyscoreAsync(key, min, max).map(function (item) {
    return {
      deviceId: deviceId,
      metric: metric,
      data: item.split("-")[0]
    }
  });
}


var signals = _
  .chain(statsOfToday)
  .pluck('deviceId')
  .map(function(deviceId) {
    var retrievalFunc = _.partial(getDeviceMetricAsync, deviceId, _, 20160215, 20160216);
    return _.map(metrics, retrievalFunc);
  })
  .flatten()
  .value();

注意:redis.zrangebyscoreAsync(key, min, max).map(...)假定RedisAPI使用Bluebird promises,即var Promise = require('Bluebird');应该在导入Redis之前完成。

普通的 Promise API 不支持 .map(),如果 Redis 有自己的 Promise 实现,您必须手动执行此操作。