在Highland.js中的上下文
我喜欢Highland.js和一般的反应式编程风格。 我正在努力与上下文的损失,我试图确定如何优雅地处理模式中的目标是放弃状态的上下文。
举个例子,我在Amazon Web Services中有一个我的账户数组。
var accounts = [{accessId:"12345","secretKey":"abc123","account":"foo"}, {accessId:"34512","secretKey":"def456","account":"bar"}];
我的目标是基本上创build一个运行在一个地区的所有EC2实例的电子表格。 像这样的东西。
Account | Instance Size --------- | ------------- foo | m3.xlarge foo | c3.medium bar | t2.small
一般的工作stream程是
- 浏览每个帐户
- 调用ec2DescribeInstaces
- 以某种方式将每个ec2DescribeInstancescallback映射到最终输出的帐户名称
在普通的JavaScript中,我们会在这里做循环,所以当我们每次调用ec2DescribeInstances时,都会存在一个上下文
for ( account in accounts ) { var instances = ec2DescribeInstaces(account); for ( instance in instances ) { results.push({account:account.name, instanceSize: instance.size}); } }
从我在反应式编程中所理解的,我会做这样的事情
_(accounts) .map(ec2DescribeInstaces) .parallel(2) .each(function(result) { results.push(result); });
任何指导? 所以在这个链条的最后,我有亚马逊的实例。 但我不确定如何将这些名称绑定到账户上。 我知道我可以破解这个获得价值,但我正在寻找最佳实践和一些优雅。
所以@Bergi,如下? 这将基本上返回一个“上下文”对象与我需要的数据,除了从亚马逊回来的数据。 我唯一担心的是,如果我在整个链中传递一个上下文,那么我们将进行大量的数据采集,填充和打包。
ec2DescribeInstances = _.wrapCallback(function(accountData, callback) { // we remove the extraneous account name using a pick var ec2 = new AWS.EC2(lodash.pick(accountData,'accessKeyId','secretAccessKey')); ec2.describeInstances({ Filters: [{Name:'instance-state-name', Values:['running']}] }, function(err,data) { if ( err ) return callback(err); // here I create an object that wraps the AWS response callback(null, {"account":accountData.alias, "data": data}) }); });
你可以使用_.zip()
,它接受两个stream并返回一个stream对。 因此,使用您的生成ec2Instances的stream作为示例:
var ec2InstanceStream = _(accounts) .map(ec2DescribeInstaces) .parallel(2); _(acounts).zip(ec2InstanceStream); .each(function(result) { //Each result: [{ /* original account */ }, { /* ec2 instance */ }] results.push(result); });
所以,现在你拥有了一切。 你当然可以做得更好。 例如,如果要将每个对合并为一个对象,则可以在.each()
步骤之前添加一个.map(_.extend)
,以将这些对象合并到一起。
我将嵌套操作来获得一些范围:
const h = require("highland"); const ec2DescribeInstaces = h.wrapCallback(function(account, cb) { return cb(null, [{ size: account.size }, { size: ++account.size }]); }); const accounts = [{ size: 1, name: 1 }, { size: 2, name: 2 }, { size: 3, name: 3 }]; var results = []; h(accounts) .map(account => { return ec2DescribeInstaces(account) .flatten() .map(instance => ({ account: account.name, instanceSize: instance.size, })); }) .parallel(2) .tap(results.push.bind(results)) .collect() .toCallback(h.log)
通过这种方式,您仍然可以了解在使用该instance
每个instance
与instance
个人account
相关的信息,并且由于map
的结果是stream,因此这些数据stream仍将以并行方式运行。
另外,我认为这是一个广泛适用的方法。 无论您返回多less个实例,或者想要为每个帐户执行多less事情都无关紧要。 例如,如果您需要为帐户上的每个实例的results
推送两个值,则可以返回parallel(2)
来自map
两个stream,并且不需要完全重新调整您的方法,只需调整行为即可。